home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / mailindx.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  113KB  |  4,321 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: mailindx.c,v 4.209 1996/03/15 07:13:42 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    USENET News reading additions in part by L Lundblade / NorthWestNet, 1993
  32.    lgl@nwnet.net
  33.  
  34.    Pine is in part based on The Elm Mail System:
  35.     ***********************************************************************
  36.     *  The Elm Mail System  -  Revision: 2.13                             *
  37.     *                                                                     *
  38.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  39.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  40.     ***********************************************************************
  41.  
  42.  
  43.   ----------------------------------------------------------------------*/
  44.  
  45. /*======================================================================
  46.     mailindx.c
  47.     Implements the mail index screen
  48.      - most code here builds the header list and displays it
  49.  
  50.  ====*/
  51.  
  52. #include "headers.h"
  53.  
  54.  
  55. static struct key index_keys[] = 
  56.        {{"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  57.     {"M","Main Menu",KS_MAINMENU},    {"V","[ViewMsg]",KS_VIEW},
  58.     {"P","PrevMsg",KS_PREVMSG},    {"N","NextMsg",KS_NEXTMSG},
  59.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  60.     {"D","Delete",KS_DELETE},    {"U","Undelete",KS_UNDELETE},
  61.     {"R","Reply",KS_REPLY},        {"F","Forward",KS_FORWARD},
  62.  
  63.     {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  64.     {"Q","Quit",KS_EXIT},        {"C","Compose",KS_COMPOSER},
  65.     {"L","ListFldrs",KS_FLDRLIST},    {"G","GotoFldr",KS_GOTOFLDR},
  66.     {"Tab","NextNew",KS_NONE},    {"W","WhereIs",KS_WHEREIS},
  67.     {"Y","prYnt",KS_PRINT},        {"T","TakeAddr",KS_TAKEADDR},
  68.     {"S","Save",KS_SAVE},        {"E","Export",KS_EXPORT},
  69.  
  70.     {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  71.     {"X",NULL,KS_NONE},        {"&","unXclude",KS_NONE},
  72.     {";","Select",KS_SELECT},    {"A","Apply",KS_APPLY},
  73.     {"$","SortIndex",KS_SORT},    {"J","Jump",KS_JUMPTOMSG},
  74.     {"H","HdrMode",KS_HDRMODE},    {"B","Bounce",KS_BOUNCE},
  75.     {"*","Flag",KS_FLAG},        {"|","Pipe",KS_NONE},
  76.  
  77.     {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  78.     {":","SelectCur",KS_SELECTCUR},    {"Z","ZoomMode",KS_NONE},
  79.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  80.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  81.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  82.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  83. INST_KEY_MENU(index_keymenu, index_keys);
  84. #define PREVM_KEY 4
  85. #define NEXTM_KEY 5
  86. #define EXCLUDE_KEY 26
  87. #define UNEXCLUDE_KEY 27
  88. #define SELECT_KEY 28
  89. #define APPLY_KEY 29
  90. #define VIEW_FULL_HEADERS_KEY 32
  91. #define BOUNCE_KEY 33
  92. #define FLAG_KEY 34
  93. #define VIEW_PIPE_KEY 35
  94. #define ZOOM_KEY 39
  95.  
  96. static struct key nr_anon_index_keys[] = 
  97.        {{"?","Help",KS_SCREENHELP},    {"W","WhereIs",KS_WHEREIS},
  98.     {"Q","Quit",KS_EXIT},        {"V","[ViewMsg]",KS_VIEW},
  99.     {"P","PrevMsg",KS_PREVMSG},    {"N","NextMsg",KS_NEXTMSG},
  100.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  101.     {"F","Fwd Email",KS_FORWARD},    {"J","Jump",KS_JUMPTOMSG},
  102.     {"$","SortIndex",KS_SORT},    {NULL,NULL,KS_NONE}};
  103. INST_KEY_MENU(nr_anon_index_keymenu, nr_anon_index_keys);
  104.  
  105. static struct key nr_index_keys[] = 
  106.        {{"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  107.     {"Q","Quit",KS_EXIT},        {"V","[ViewMsg]",KS_VIEW},
  108.     {"P","PrevMsg",KS_PREVMSG},    {"N","NextMsg",KS_NEXTMSG},
  109.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  110.     {"F","Fwd Email",KS_FORWARD},    {"J","Jump",KS_JUMPTOMSG},
  111.     {"Y","prYnt",KS_PRINT},        {"S","Save",KS_SAVE},
  112.  
  113.     {"?","Help",KS_SCREENHELP},    {"O","OTHER CMDS",KS_NONE},
  114.     {"E","Export",KS_EXPORT},    {"C","Compose",KS_COMPOSER},
  115.     {"$","SortIndex",KS_SORT},    {NULL,NULL,KS_NONE},
  116.     {NULL,NULL,KS_NONE},        {"W","WhereIs",KS_WHEREIS},
  117.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE},
  118.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  119. INST_KEY_MENU(nr_index_keymenu, nr_index_keys);
  120.   
  121. static struct key simple_index_keys[] = 
  122.        {{"?","Help",KS_SCREENHELP},    {NULL,NULL,KS_NONE},
  123.     {"E","ExitSelect",KS_EXITMODE},    {"S","[Select]",KS_SELECT},
  124.     {"P","PrevMsg",KS_PREVMSG},    {"N","NextMsg",KS_NEXTMSG},
  125.     {"-","PrevPage",KS_PREVPAGE},    {"Spc","NextPage",KS_NEXTPAGE},
  126.     {"D","Delete",KS_DELETE},    {"U","Undelete",KS_UNDELETE},
  127.     {NULL,NULL,KS_NONE},        {NULL,NULL,KS_NONE}};
  128. INST_KEY_MENU(simple_index_keymenu, simple_index_keys);
  129.  
  130.  
  131. static OtherMenu what_keymenu = FirstMenu;
  132. jmp_buf jump_past_qsort;
  133.  
  134.  
  135. /*-----------
  136.   Saved state to redraw message index body 
  137.   ----*/
  138. struct entry_state {
  139.     unsigned hilite:1;
  140.     unsigned bolded:1;
  141.     long     id;
  142. };
  143.  
  144.  
  145. #define MAXIFLDS 20  /* max number of fields in index format */
  146. static struct index_state {
  147.     long        msg_at_top,
  148.             lines_per_page;
  149.     struct      entry_state *entry_state;
  150.     MSGNO_S    *msgmap;
  151.     MAILSTREAM *stream;
  152.     int         status_col;        /* column for select X's */
  153. } *current_index_state = NULL;
  154.  
  155. static int get_body_for_index;
  156.  
  157.  
  158. /*
  159.  * cache used by get_sub() so it doesn't have to do a fetchstructure
  160.  * and malloc/free for every qsort comparison...
  161.  */
  162. #if    defined(DOS) && !defined(_WINDOWS)
  163. #define    SUB_CACHE_LEN    128
  164. typedef struct {
  165.     short used;                /* whether entry's been used or not */
  166.     char  buf[SUB_CACHE_LEN];        /* actual subject string */
  167. } scache_ent;
  168.  
  169.  
  170. static struct scache {
  171.     short    last;            /* last one referenced */
  172.     long    size,            /* number of elements */
  173.         msgno[2];        /* which message in which entry */
  174.     scache_ent  ent[2];            /* subject and cache info for msg */
  175.     char       *cname;            /* file containing cache */
  176.     FILE       *cfile;
  177. } *scache = NULL;
  178. #else
  179. static struct scache {
  180.     long   size;
  181.     char **ent;
  182. } *scache = NULL;
  183. #endif
  184.  
  185.  
  186. /*
  187.  * Internal prototypes
  188.  */
  189. int     update_index PROTO((struct pine *, struct index_state *));
  190. int     index_scroll_up PROTO((long));
  191. int     index_scroll_down PROTO((long));
  192. int     index_scroll_to_pos PROTO((long));
  193. long     top_ent_calc PROTO((MAILSTREAM *, MSGNO_S *, long, long));
  194. char    *get_sub PROTO((long));
  195. int      compare_subjects PROTO((const QSType *, const QSType *));
  196. int      compare_subject_2 PROTO((const QSType *, const QSType *));
  197. int      compare_from PROTO((const QSType *, const QSType *));
  198. int      compare_to PROTO((const QSType *, const QSType *));
  199. int      compare_cc PROTO((const QSType *, const QSType *));
  200. int      compare_message_dates PROTO((const QSType *, const QSType *));
  201. int      compare_size PROTO((const QSType *, const QSType *));
  202. int      compare_arrival PROTO((const QSType *, const QSType *));
  203. HLINE_S *get_index_cache PROTO((long));
  204. long     line_hash PROTO((char *));
  205. int     set_index_addr PROTO((MAILSTREAM *, long, char *, ADDRESS *, char *,
  206.                    int, char *));
  207. int      i_cache_size PROTO((long));
  208. int      i_cache_width PROTO(());
  209. void     setup_header_widths PROTO((INDEX_COL_S *, int, long));
  210. int     parse_index_format PROTO((char *, INDEX_COL_S **));
  211. void     set_need_format_setup();
  212. void     clear_need_format_setup();
  213. int     check_need_format_setup();
  214. void     msgno_flush_selected PROTO((MSGNO_S *, long));
  215. void     second_subject_sort PROTO((long *, long, long));
  216. void     init_subject_cache PROTO((long));
  217. int     subject_cache_slot PROTO((long));
  218. char    *subject_cache_ent PROTO((long));
  219. char    *subject_cache_add PROTO((long, char *));
  220. void     clear_subject_cache PROTO(());
  221. #ifdef    DOS
  222. void     i_cache_hit PROTO((long));
  223. void     icread PROTO((void));
  224. void     icwrite PROTO((void));
  225. #ifdef    _WINDOWS
  226. int     index_scroll_callback PROTO((int,long));
  227. int     index_gettext_callback PROTO((char *, void **, long *, int *));
  228. #endif
  229. #endif
  230. long     seconds_since_epoch PROTO((long));
  231.  
  232.  
  233.  
  234.  
  235. /*----------------------------------------------------------------------
  236.  
  237.  
  238.   ----*/
  239. void
  240. do_index_border(cntxt, folder, stream, msgmap, style, which_keys, flags)
  241.      CONTEXT_S   *cntxt;
  242.      char        *folder;
  243.      MAILSTREAM  *stream;
  244.      MSGNO_S     *msgmap;
  245.      IndexType    style;
  246.      int         *which_keys, flags;
  247. {
  248.     if(flags & INDX_CLEAR)
  249.       ClearScreen();
  250.  
  251.     if(flags & INDX_HEADER)
  252.       set_titlebar((stream == ps_global->mail_stream)
  253.              ? (style == MsgIndex || style == MultiMsgIndex)
  254.                  ? "FOLDER INDEX"
  255.              : "ZOOMED FOLDER INDEX"
  256.              : (!strcmp(folder, INTERRUPTED_MAIL))
  257.              ? "COMPOSE: SELECT INTERRUPTED"
  258.              : "COMPOSE: SELECT POSTPONED",
  259.            stream, cntxt, folder, msgmap, 1, MessageNumber, 0, 0);
  260.  
  261.     if(flags & INDX_FOOTER) {
  262.     struct key_menu *km;
  263.     bitmap_t     bitmap;
  264.  
  265.     setbitmap(bitmap);
  266.     if(ps_global->anonymous)
  267.       km = &nr_anon_index_keymenu;
  268.     else if(ps_global->nr_mode)
  269.           km = &nr_index_keymenu;
  270.     else if(ps_global->mail_stream != stream)
  271.       km = &simple_index_keymenu;
  272.     else{
  273.         km = &index_keymenu;
  274.  
  275. #ifndef DOS
  276.         if(F_OFF(F_ENABLE_PIPE,ps_global))
  277. #endif
  278.           clrbitn(VIEW_PIPE_KEY, bitmap);  /* always clear for DOS */
  279.         if(F_OFF(F_ENABLE_FULL_HDR,ps_global))
  280.           clrbitn(VIEW_FULL_HEADERS_KEY, bitmap);
  281.         if(F_OFF(F_ENABLE_BOUNCE,ps_global))
  282.           clrbitn(BOUNCE_KEY, bitmap);
  283.         if(F_OFF(F_ENABLE_FLAG,ps_global))
  284.           clrbitn(FLAG_KEY, bitmap);
  285.         if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
  286.         clrbitn(SELECT_KEY, bitmap);
  287.         clrbitn(APPLY_KEY, bitmap);
  288.  
  289.         /*
  290.          * Since "Zoom" is alone on the last keymenu page
  291.          * if it's not enabled, hide that menu altogether
  292.          */
  293.         if(style != ZoomIndex){
  294.             clrbitn(ZOOM_KEY, bitmap);
  295.             km->how_many = 3;
  296.         }
  297.         else
  298.           km->how_many = 4;
  299.         }
  300.         else
  301.           km->how_many = 4;
  302.  
  303.         if(IS_NEWS(stream)){
  304.         index_keys[EXCLUDE_KEY].label = "eXclude";
  305.         KS_OSDATASET(&index_keys[EXCLUDE_KEY], KS_NONE);
  306.         }
  307.         else {
  308.         clrbitn(UNEXCLUDE_KEY, bitmap);
  309.         index_keys[EXCLUDE_KEY].label = "eXpunge";
  310.         KS_OSDATASET(&index_keys[EXCLUDE_KEY], KS_EXPUNGE);
  311.         }
  312.  
  313.         if(style == MultiMsgIndex){
  314.         clrbitn(PREVM_KEY, bitmap);
  315.         clrbitn(NEXTM_KEY, bitmap);
  316.         }
  317.     }
  318.         draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
  319.                1-FOOTER_ROWS(ps_global), 0, what_keymenu, 0);
  320.     what_keymenu = SameTwelve;
  321.     if(which_keys)
  322.       *which_keys = km->which;  /* pass back to caller */
  323.     }
  324. }
  325.  
  326.       
  327.     
  328. /*----------------------------------------------------------------------
  329.         Main loop executing commands for the mail index screen
  330.  
  331.    Args: state -- the pine_state structure for next/prev screen pointers
  332.                   and to pass to the index manager...
  333.  ----*/
  334.  
  335. void
  336. mail_index_screen(state)
  337.      struct pine *state;
  338. {
  339.     dprint(1, (debugfile, "\n\n ---- MAIL INDEX ----\n"));
  340.     if(!state->mail_stream) {
  341.     q_status_message(SM_ORDER, 0, 3, "No folder is currently open");
  342.         state->prev_screen = mail_index_screen;
  343.     state->next_screen = main_menu_screen;
  344.     return;
  345.     }
  346.  
  347.     index_lister(state, state->context_current, state->cur_folder,
  348.          state->mail_stream, state->msgmap);
  349.     state->prev_screen = mail_index_screen;
  350. }
  351.  
  352.  
  353.  
  354. /*----------------------------------------------------------------------
  355.         Main loop executing commands for the mail index screen
  356.  
  357.    Args: state -- pine_state structure for display flags and such
  358.          msgmap -- c-client/pine message number mapping struct
  359.  ----*/
  360.  
  361. int
  362. index_lister(state, cntxt, folder, stream, msgmap)
  363.      struct pine *state;
  364.      CONTEXT_S   *cntxt;
  365.      char        *folder;
  366.      MAILSTREAM  *stream;
  367.      MSGNO_S     *msgmap;
  368. {
  369.     int         ch, orig_ch, which_keys, force, cur_row, cur_col, km_popped;
  370.     long     i, j, k, old_max_msgno;
  371.     IndexType    style, old_style = MsgIndex;
  372.     struct index_state id;
  373. #if defined(DOS) || defined(OS2)
  374.     extern void (*while_waiting)();
  375. #endif
  376.  
  377.     dprint(1, (debugfile, "\n\n ---- INDEX MANAGER ----\n"));
  378.     
  379.     ch                    = 'x';    /* For displaying msg 1st time thru */
  380.     force                 = 0;
  381.     km_popped             = 0;
  382.     state->mangled_screen = 1;
  383.     what_keymenu          = FirstMenu;
  384.     memset((void *)&id, 0, sizeof(struct index_state));
  385.     current_index_state   = &id;
  386.     id.msgmap    = msgmap;
  387.  
  388.     if((id.stream = stream) != state->mail_stream)
  389.       clear_index_cache();    /* BUG: should better tie stream to cache */
  390.  
  391.     set_need_format_setup();
  392.  
  393.     while (1) {
  394.     if(km_popped){
  395.         km_popped--;
  396.         if(km_popped == 0){
  397.         clearfooter(state);
  398.         if(!state->mangled_body
  399.            && id.entry_state
  400.            && id.lines_per_page > 1){
  401.             id.entry_state[id.lines_per_page-2].id = -1;
  402.             id.entry_state[id.lines_per_page-1].id = -1;
  403.         }
  404.         else
  405.           state->mangled_body = 1;
  406.         }
  407.     }
  408.  
  409.     old_max_msgno = mn_get_total(msgmap);
  410.  
  411.     /*------- Check for new mail -------*/
  412.         new_mail(force, NM_TIMING(ch), 1);
  413.     force = 0;            /* may not need to next time around */
  414.  
  415.     /*
  416.      * This is only actually necessary if this causes the width of the
  417.      * message number field to change.  That is, it depends on the
  418.      * format the user is using as well as on the max_msgno.  Since it
  419.      * should be rare, we'll just do it whenever it happens.
  420.      * Also have to check for a reduction in max_msgno when we expunge.
  421.      */
  422.     if(old_max_msgno < 1000L && mn_get_total(msgmap) >= 1000L
  423.        || old_max_msgno < 10000L && mn_get_total(msgmap) >= 10000L
  424.        || old_max_msgno < 100000L && mn_get_total(msgmap) >= 100000L){
  425.         clear_index_cache();
  426.         state->mangled_body = 1;
  427.         }
  428.  
  429.         if(streams_died())
  430.           state->mangled_header = 1;
  431.  
  432.         if(state->mangled_screen){
  433.             state->mangled_header = 1;
  434.             state->mangled_body   = 1;
  435.             state->mangled_footer = 1;
  436.             state->mangled_screen = 0;
  437.         }
  438.  
  439.     /*
  440.      * events may have occured that require us to shift from
  441.      * mode to another...
  442.      */
  443.     style = (any_lflagged(msgmap, MN_HIDE))
  444.           ? ZoomIndex
  445.           : (mn_total_cur(msgmap) > 1L) ? MultiMsgIndex : MsgIndex;
  446.     if(style != old_style){
  447.             state->mangled_header = 1;
  448.             state->mangled_footer = 1;
  449.         old_style = style;
  450.         id.msg_at_top = 0L;
  451.     }
  452.  
  453.         /*------------ Update the title bar -----------*/
  454.     if(state->mangled_header) {
  455.             do_index_border(cntxt, folder, stream, msgmap, style, NULL,
  456.                 INDX_HEADER);
  457.         state->mangled_header = 0;
  458.     } 
  459.     else if(mn_get_total(msgmap) > 0) {
  460.         /*
  461.          * No fetchstructure necessary before mail_elt as the elt's
  462.          * should have been loaded with the correct flags when we did
  463.          * a fetchflags as part of get_lflags the first time we painted
  464.          * the index...
  465.          */
  466.         update_titlebar_message(msgmap);
  467.             update_titlebar_status(mail_elt(stream, mn_m2raw(msgmap,
  468.                              mn_get_cur(msgmap))));
  469.     }
  470.  
  471.     current_index_state = &id;
  472.  
  473.         /*------------ draw the index body ---------------*/
  474.     cur_row = update_index(state, &id);
  475.     if(F_OFF(F_SHOW_CURSOR, state)){
  476.         cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
  477.         cur_col = 0;
  478.     }
  479.     else if(id.status_col >= 0)
  480.       cur_col = min(id.status_col, state->ttyo->screen_cols-1);
  481.  
  482.         ps_global->redrawer = redraw_index_body;
  483.  
  484.         /*------------ draw the footer/key menus ---------------*/
  485.     if(state->mangled_footer) {
  486.             if(!state->painted_footer_on_startup){
  487.         if(km_popped){
  488.             FOOTER_ROWS(state) = 3;
  489.             clearfooter(state);
  490.         }
  491.  
  492.         do_index_border(cntxt, folder, stream, msgmap, style,
  493.                   &which_keys, INDX_FOOTER);
  494.         if(km_popped){
  495.             FOOTER_ROWS(state) = 1;
  496.             mark_keymenu_dirty();
  497.         }
  498.         }
  499.  
  500.         state->mangled_footer = 0;
  501.     }
  502.  
  503.         state->painted_body_on_startup   = 0;
  504.         state->painted_footer_on_startup = 0;
  505.  
  506.     /*-- Display any queued message (eg, new mail, command result --*/
  507.     if(km_popped){
  508.         FOOTER_ROWS(state) = 3;
  509.         mark_status_unknown();
  510.     }
  511.  
  512.         display_message(ch);
  513.     if(km_popped){
  514.         FOOTER_ROWS(state) = 1;
  515.         mark_status_unknown();
  516.     }
  517.  
  518.     if(F_ON(F_SHOW_CURSOR, state) && cur_row < 0){
  519.         q_status_message(SM_ORDER,
  520.         (ch==NO_OP_IDLE || ch==NO_OP_COMMAND) ? 0 : 3, 5,
  521.         "No messages in folder");
  522.         cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
  523.         display_message(ch);
  524.     }
  525.  
  526.     MoveCursor(cur_row, cur_col);
  527.  
  528.         /* Let read_command do the fflush(stdout) */
  529.  
  530.         /*---------- Read command and validate it ----------------*/
  531. #ifdef    MOUSE
  532.     if(stream == ps_global->mail_stream){    /* prime the handler */
  533.         mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
  534.         register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
  535.                state->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
  536.                state->ttyo->screen_cols);
  537.                
  538.     }
  539. #endif
  540. #if defined(DOS) || defined(OS2)
  541.     /*
  542.      * AND pre-build header lines.  This works just fine under
  543.      * DOS since we wait for characters in a loop. Something will
  544.          * will have to change under UNIX if we want to do the same.
  545.      */
  546.     while_waiting = build_header_cache;
  547. #ifdef    _WINDOWS
  548.     while_waiting = NULL;
  549.     mswin_setscrollcallback (index_scroll_callback);
  550. #endif
  551. #endif
  552.     ch = read_command();
  553. #ifdef    MOUSE
  554.     if(stream == ps_global->mail_stream)
  555.       clear_mfunc(mouse_in_content);
  556. #endif
  557. #if defined(DOS) || defined(OS2)
  558.     while_waiting = NULL;
  559. #ifdef    _WINDOWS
  560.     mswin_setscrollcallback(NULL);
  561. #endif
  562. #endif
  563.  
  564.         orig_ch = ch;
  565.  
  566.     if(ch < 0x0100 && isupper(ch))        /* force lower case */
  567.       ch = tolower(ch);
  568.     else if(ch >= PF1 && ch <= PF12 && which_keys > 0 && which_keys < 4)
  569.       ch = (which_keys == 1) ? PF2OPF(ch) :    /* map f-key to menu page */
  570.         (which_keys == 2) ? PF2OOPF(ch) : PF2OOOPF(ch);
  571.  
  572.     ch = validatekeys(ch);
  573.  
  574.     if(km_popped)
  575.       switch(ch){
  576.         case NO_OP_IDLE:
  577.         case NO_OP_COMMAND: 
  578.         case PF2:
  579.         case OPF2:
  580.             case OOPF2:
  581.             case OOOPF2:
  582.         case 'o' :
  583.         case KEY_RESIZE:
  584.         case ctrl('L'):
  585.           km_popped++;
  586.           break;
  587.         
  588.         default:
  589.           clearfooter(state);
  590.           break;
  591.       }
  592.  
  593.     /*----------- Execute the command ------------------*/
  594.     switch(ch) {
  595.  
  596.             /*---------- Roll keymenu ----------*/
  597.           case PF2:
  598.           case OPF2:
  599.           case OOPF2:
  600.           case OOOPF2:
  601.       case 'o':
  602.             if(ps_global->anonymous) {
  603.           if(ch == PF2)
  604.         ch = 'w';
  605.               goto df;
  606.         }
  607.             if(ch == 'o')
  608.           warn_other_cmds();
  609.         what_keymenu = NextTwelve;
  610.         state->mangled_footer = 1;
  611.         break;
  612.  
  613.  
  614.             /*---------- Scroll back up ----------*/
  615.       case PF7:
  616.       case '-' :
  617.           case ctrl('Y'): 
  618.       case KEY_PGUP:
  619.       pageup:
  620.         j = -1L;
  621.         for(k = i = id.msg_at_top; ; i--){
  622.         if(!get_lflag(stream, msgmap, i, MN_HIDE)){
  623.             k = i;
  624.             if(++j >= id.lines_per_page){
  625.             if((id.msg_at_top = i) == 1L)
  626.               q_status_message(SM_ORDER, 0, 1, "First Index page");
  627.  
  628.             break;
  629.             }
  630.            }
  631.  
  632.         if(i <= 1L){
  633.             if(mn_get_cur(msgmap) == 1L)
  634.               q_status_message(SM_ORDER, 0, 1,
  635.               "Already at start of Index");
  636.  
  637.             break;
  638.         }
  639.         }
  640.  
  641.         if(mn_total_cur(msgmap) == 1L)
  642.           mn_set_cur(msgmap, k);
  643.  
  644.         break;
  645.  
  646.  
  647.             /*---------- Scroll forward, next page ----------*/
  648.       case PF8:
  649.       case '+':
  650.           case ctrl('V'): 
  651.       case KEY_PGDN:
  652.       case ' ':
  653.       pagedown:
  654.         j = -1L;
  655.         for(k = i = id.msg_at_top; ; i++){
  656.         if(!get_lflag(stream, msgmap, i, MN_HIDE)){
  657.             k = i;
  658.             if(++j >= id.lines_per_page){
  659.             if(i+id.lines_per_page >= mn_get_total(msgmap))
  660.               q_status_message(SM_ORDER, 0, 1, "Last Index page");
  661.  
  662.             id.msg_at_top = i;
  663.             break;
  664.             }
  665.         }
  666.  
  667.         if(i >= mn_get_total(msgmap)){
  668.             if(mn_get_cur(msgmap) == k)
  669.               q_status_message(SM_ORDER,0,1,"Already at end of Index");
  670.  
  671.             break;
  672.         }
  673.         }
  674.  
  675.         if(mn_total_cur(msgmap) == 1L)
  676.           mn_set_cur(msgmap, k);
  677.  
  678.         break;
  679.  
  680. #ifdef MOUSE        
  681.       case KEY_MOUSE:
  682.         {
  683.           static int lastWind;
  684.           MOUSEPRESS mp;
  685.           void    *text;
  686.           long    len;
  687.           int    format;
  688.           
  689.  
  690.           mouse_get_last (NULL, &mp);
  691.           mp.row -= 2;
  692.           if (mp.doubleclick && mp.button != M_BUTTON_RIGHT) {
  693.           orig_ch = ch = F_ON(F_USE_FK, ps_global) ? PF4 : 'v';
  694.           goto df;
  695.           }
  696.           else{
  697.         for(i = id.msg_at_top;
  698.             mp.row >= 0 && i <= mn_get_total(msgmap);
  699.             i++)
  700.           if(!get_lflag(stream, msgmap, i, MN_HIDE)){
  701.               mp.row--;
  702.               if(mn_total_cur(msgmap) == 1L)
  703.             mn_set_cur(msgmap, i);
  704.           }
  705. #ifdef _WINDOWS             
  706.         if (mp.button == M_BUTTON_RIGHT) {
  707.           char title[GETTEXT_TITLELEN+1];
  708.  
  709.             /* Launch text in alt window. */
  710.           if (mp.flags & M_KEY_CONTROL)
  711.             lastWind = 0;
  712.           if (index_gettext_callback (title, &text, &len, &format)) {
  713.             if (format == GETTEXT_TEXT) 
  714.               lastWind = mswin_displaytext (title, text, (size_t)len, 
  715.                           NULL, lastWind, 0);
  716.             else if (format == GETTEXT_LINES) 
  717.               lastWind = mswin_displaytext (title, NULL, 0, 
  718.                           text, lastWind, 0);
  719.           }
  720.         }
  721. #endif /* _WINDOWS */
  722.           }
  723.         }
  724.  
  725.         break;
  726. #endif    /* MOUSE */
  727.  
  728.             /*---------- Redraw/resize ----------*/
  729.           case KEY_RESIZE:
  730.         clear_index_cache();
  731.       case ctrl('L'):
  732.         mark_status_dirty();
  733.         mark_keymenu_dirty();
  734.         mark_titlebar_dirty();
  735.             state->mangled_screen = 1;        /* force repaint and... */
  736.         if(ch == ctrl('L'))
  737.           force = 1;            /* check for new mail on ^L */
  738.  
  739.             break;
  740.  
  741.  
  742.             /*---------- No op command ----------*/
  743.           case NO_OP_IDLE:
  744.       case NO_OP_COMMAND:
  745.             break;    /* no op check for new mail */
  746.  
  747.  
  748.             /*---------- Default -- all other command ----------*/
  749.           default:
  750.           df:
  751.         if((ch == '?' || ch == ctrl('G') || ch == PF1
  752.             || ch == OPF1 || ch == OOPF1 || ch == OOOPF1)
  753.         && FOOTER_ROWS(state) == 1
  754.         && km_popped == 0){
  755.         km_popped = 2;
  756.         mark_status_unknown();
  757.         mark_keymenu_dirty();
  758.         state->mangled_footer = 1;
  759.         }
  760.         else if(stream == state->mail_stream){
  761.         process_cmd(state, msgmap, ch,
  762.                 (style == MsgIndex || style == MultiMsgIndex)?1:2,
  763.                 orig_ch, &force);
  764.         if(state->next_screen != SCREEN_FUN_NULL){
  765.             current_index_state = NULL;
  766.             if(id.entry_state)
  767.               fs_give((void **)&(id.entry_state));
  768.  
  769.             return(0);
  770.         }
  771.         else{
  772.             if(stream != state->mail_stream){
  773.             /*
  774.              * Must have had an failed open.  repair our
  775.              * pointers...
  776.              */
  777.             id.stream = stream = state->mail_stream;
  778.             id.msgmap = msgmap = state->msgmap;
  779.             }
  780.  
  781.             current_index_state = &id;
  782.         }
  783.  
  784.         /*
  785.          * Page framing exception handling here.  If we
  786.          * did something that should scroll-by-a-line, frame
  787.          * the page by hand here rather than leave it to the
  788.          * page-by-page framing in update_index()...
  789.          */
  790.         switch(ch){
  791.           case KEY_DOWN:
  792.           case ctrl('N'):
  793.             for(j = 0L, k = i = id.msg_at_top; ; i++){
  794.             if(!get_lflag(stream, msgmap, i, MN_HIDE)){
  795.                 k = i;
  796.                 if(j++ >= id.lines_per_page)
  797.                   break;
  798.             }
  799.  
  800.             if(i >= mn_get_total(msgmap)){
  801.                 k = 0L;        /* don't scroll */
  802.                 break;
  803.             }
  804.             }
  805.  
  806.             if(k && mn_get_cur(msgmap) >= k)
  807.               index_scroll_down(1L);
  808.  
  809.             break;
  810.  
  811.           case ctrl('P'):
  812.           case KEY_UP:
  813.             if(mn_get_cur(msgmap) < id.msg_at_top)
  814.               index_scroll_up(1L);
  815.  
  816.             break;
  817.  
  818.           default:
  819.             break;
  820.         }
  821.         }
  822.         else{            /* special processing */
  823.         switch(ch){
  824.           case '?':        /* help! */
  825.           case PF1:
  826.           case ctrl('G'):
  827.             helper(h_simple_index,
  828.                (!strcmp(folder, INTERRUPTED_MAIL))
  829.                  ? "HELP FOR SELECTING INTERRUPTED MSG"
  830.                  : "HELP FOR SELECTING POSTPONED MSG",
  831.                1);
  832.             state->mangled_screen = 1;
  833.             break;
  834.  
  835.           case 'd':        /* delete */
  836.           case 'D':
  837.           case PF9:
  838.             dprint(3,(debugfile, "Special delete: msg %s\n",
  839.                   long2string(mn_get_cur(msgmap))));
  840.             {
  841.             MESSAGECACHE *mc;
  842.             long          raw;
  843.             int          del = 0;
  844.  
  845.             raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
  846.             if(!mail_elt(stream, raw)->deleted){
  847.                 clear_index_cache_ent(mn_get_cur(msgmap));
  848.                 mail_setflag(stream,long2string(raw),"\\DELETED");
  849.                 update_titlebar_status(mail_elt(stream, raw));
  850.                 del++;
  851.             }
  852.  
  853.             q_status_message2(SM_ORDER, 0, 1,
  854.                       "Message %s %sdeleted",
  855.                       long2string(mn_get_cur(msgmap)),
  856.                       (del) ? "" : "already ");
  857.             }
  858.  
  859.             break;
  860.  
  861.           case 'u':        /* UNdelete */
  862.           case 'U':
  863.           case PF10:
  864.             dprint(3,(debugfile, "Special UNdelete: msg %s\n",
  865.                   long2string(mn_get_cur(msgmap))));
  866.             {
  867.             MESSAGECACHE *mc;
  868.             long          raw;
  869.             int          del = 0;
  870.  
  871.             raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
  872.             if(mail_elt(stream, raw)->deleted){
  873.                 clear_index_cache_ent(mn_get_cur(msgmap));
  874.                 mail_clearflag(stream, long2string(raw),
  875.                        "\\DELETED");
  876.                 update_titlebar_status(mail_elt(stream, raw));
  877.                 del++;
  878.             }
  879.  
  880.             q_status_message2(SM_ORDER, 0, 1,
  881.                       "Message %s %sdeleted",
  882.                       long2string(mn_get_cur(msgmap)),
  883.                       (del) ? "UN" : "NOT ");
  884.             }
  885.  
  886.             break;
  887.  
  888.           case 'e':        /* exit */
  889.           case 'E':
  890.           case PF3:
  891.             return(1);
  892.             break;
  893.  
  894.           case 's':        /* select */
  895.           case ctrl('M'):
  896.           case ctrl('J'):
  897.           case PF4:
  898.               return(0);
  899.  
  900.           case 'p':        /* previous */
  901.           case ctrl('P'):
  902.           case KEY_UP:
  903.           case PF5:
  904.             mn_dec_cur(stream, msgmap);
  905.             break;
  906.  
  907.           case 'n':        /* next */
  908.           case ctrl('N'):
  909.           case KEY_DOWN:
  910.           case PF6:
  911.             mn_inc_cur(stream, msgmap);
  912.             break;
  913.  
  914.           default :
  915.             bogus_command(ch, NULL);
  916.             break;
  917.         }
  918.         }
  919.     }                /* The big switch */
  920.     }                    /* the BIG while loop! */
  921. }
  922.  
  923.  
  924.  
  925. /*----------------------------------------------------------------------
  926.   Manage index body painting
  927.  
  928.   Args: state - pine struct containing selected message data
  929.     index_state - struct describing what's currently displayed
  930.  
  931.   Returns: screen row number of first highlighted message
  932.  
  933.   The idea is pretty simple.  Maintain an array of index line id's that
  934.   are displayed and their hilited state.  Decide what's to be displayed
  935.   and update the screen appropriately.  All index screen painting
  936.   is done here.  Pretty simple, huh?
  937.  ----*/
  938. int
  939. update_index(state, screen)
  940.     struct pine         *state;
  941.     struct index_state  *screen;
  942. {
  943.     int  i, retval = -1;
  944.     long n;
  945.  
  946.     if(!screen)
  947.       return(-1);
  948.  
  949. #ifdef _WINDOWS
  950.     mswin_beginupdate();
  951. #endif
  952.  
  953.     /*---- reset the works if necessary ----*/
  954.     if(state->mangled_body && screen->entry_state){
  955.     fs_give((void **)&(screen->entry_state));
  956.     screen->lines_per_page = 0;
  957.     ClearBody();
  958.     }
  959.  
  960.     state->mangled_body = 0;
  961.  
  962.     /*---- make sure we have a place to write state ----*/
  963.     if(screen->lines_per_page
  964.     != max(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
  965.                        - HEADER_ROWS(state))){
  966.     i = screen->lines_per_page;
  967.     screen->lines_per_page
  968.         = max(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
  969.                           - HEADER_ROWS(state));
  970.     if(!i){
  971.         size_t len = screen->lines_per_page * sizeof(struct entry_state);
  972.         screen->entry_state = (struct entry_state *) fs_get(len);
  973.     }
  974.     else
  975.       fs_resize((void **)&(screen->entry_state),
  976.             (size_t)screen->lines_per_page);
  977.  
  978.     for(; i < screen->lines_per_page; i++)    /* init new entries */
  979.       screen->entry_state[i].id = -1;
  980.     }
  981.  
  982.     /*---- figure out the first message on the display ----*/
  983.     if(screen->msg_at_top < 1L
  984.        || (any_lflagged(screen->msgmap, MN_HIDE) > 0L
  985.        && get_lflag(screen->stream, screen->msgmap,
  986.             screen->msg_at_top, MN_HIDE))){
  987.     screen->msg_at_top = top_ent_calc(screen->stream, screen->msgmap,
  988.                       screen->msg_at_top,
  989.                       screen->lines_per_page);
  990.     }
  991.     else if(mn_get_cur(screen->msgmap) < screen->msg_at_top){
  992.     long i, j, k;
  993.  
  994.     /* scroll back a page at a time until current is displayed */
  995.     while(mn_get_cur(screen->msgmap) < screen->msg_at_top){
  996.         for(i = screen->lines_per_page, j = screen->msg_at_top-1L, k = 0L;
  997.         i > 0L && j > 0L;
  998.         j--)
  999.           if(!get_lflag(screen->stream, screen->msgmap, j, MN_HIDE)){
  1000.           k = j;
  1001.           i--;
  1002.           }
  1003.  
  1004.         if(i == screen->lines_per_page)
  1005.           break;                /* can't scroll back ? */
  1006.         else
  1007.           screen->msg_at_top = k;
  1008.     }
  1009.     }
  1010.     else if(mn_get_cur(screen->msgmap) >= screen->msg_at_top
  1011.                              + screen->lines_per_page){
  1012.     long i, j, k;
  1013.  
  1014.     while(1){
  1015.         for(i = screen->lines_per_page, j = k = screen->msg_at_top;
  1016.         j <= mn_get_total(screen->msgmap) && i > 0L;
  1017.         j++)
  1018.           if(!get_lflag(screen->stream, screen->msgmap, j, MN_HIDE)){
  1019.           k = j;
  1020.           i--;
  1021.           }
  1022.  
  1023.         if(mn_get_cur(screen->msgmap) <= k)
  1024.           break;
  1025.         else{
  1026.         /* set msg_at_top to next displayed message */
  1027.         for(i = k + 1L; i <= mn_get_total(screen->msgmap); i++)
  1028.           if(!get_lflag(screen->stream, screen->msgmap, i, MN_HIDE)){
  1029.               k = i;
  1030.               break;
  1031.           }
  1032.  
  1033.         screen->msg_at_top = k;
  1034.         }
  1035.     }
  1036.     }
  1037.  
  1038. #ifdef    _WINDOWS
  1039.     /* Set scroll range and position.  Note that message numbers start at 1
  1040.      * while scroll position starts at 0. */
  1041.     scroll_setrange(mn_get_total(screen->msgmap) - 1L);
  1042.     scroll_setpos(screen->msg_at_top - 1L);
  1043. #endif
  1044.     
  1045.     /*---- march thru display lines, painting whatever is needed ----*/
  1046.     for(i = 0, n = screen->msg_at_top; i < (int) screen->lines_per_page; i++){
  1047.     if(n < 1 || n > mn_get_total(screen->msgmap)){
  1048.         if(screen->entry_state[i].id){
  1049.         screen->entry_state[i].hilite = 0;
  1050.         screen->entry_state[i].bolded = 0;
  1051.         screen->entry_state[i].id     = 0L;
  1052.         ClearLine(HEADER_ROWS(state) + i);
  1053.         }
  1054.     }
  1055.     else{
  1056.         int      cur = mn_is_cur(screen->msgmap, n),
  1057.              sel = get_lflag(screen->stream,screen->msgmap,n,MN_SLCT),
  1058.              status_col;
  1059.         HLINE_S *h   = build_header_line(state,
  1060.                 screen->stream,screen->msgmap,n);
  1061.  
  1062.         status_col = screen->status_col;
  1063.  
  1064.         if(h->id != screen->entry_state[i].id
  1065.            || (cur != screen->entry_state[i].hilite)
  1066.            || (sel != screen->entry_state[i].bolded)){
  1067.         MoveCursor(HEADER_ROWS(state) + i, 0);
  1068.         if(F_ON(F_FORCE_LOW_SPEED,ps_global) || ps_global->low_speed){
  1069.             MoveCursor(HEADER_ROWS(state) + i, status_col);
  1070.             Writechar((sel) ? 'X' :
  1071.                     (cur && h->line[status_col] == ' ') ?
  1072.                     '-' : h->line[status_col], 0);
  1073.             Writechar((cur) ? '>' : h->line[status_col+1], 0);
  1074.  
  1075.             if(h->id != screen->entry_state[i].id){
  1076.             if(status_col == 0)
  1077.               PutLine0(HEADER_ROWS(state) + i, 2, &h->line[2]);
  1078.             else{ /* this will rarely be set up this way */
  1079.                 char save_char1, save_char2;
  1080.  
  1081.                 save_char1 = h->line[status_col];
  1082.                 save_char2 = h->line[status_col+1];
  1083.                 h->line[status_col] = (sel) ? 'X' :
  1084.                 (cur && save_char1 == ' ') ?
  1085.                  '-' : save_char1;
  1086.                 h->line[status_col+1] = (cur) ? '>' :
  1087.                                  save_char2;
  1088.                 PutLine0(HEADER_ROWS(state) + i, 0, &h->line[0]);
  1089.                 h->line[status_col]   = save_char1;
  1090.                 h->line[status_col+1] = save_char2;
  1091.             }
  1092.             }
  1093.         }
  1094.         else{
  1095.             char *draw = h->line;
  1096.             char  save_char;
  1097.             int   drew_X = 0;
  1098.  
  1099.             if(cur)
  1100.               StartInverse();
  1101.             
  1102.             save_char = draw[status_col];
  1103.  
  1104.             if(sel && (F_OFF(F_SELECTED_SHOWN_BOLD, state)
  1105.                    || !StartBold())){
  1106.             draw[status_col] = 'X';
  1107.             drew_X++;
  1108.             }
  1109.  
  1110.             Write_to_screen(draw);
  1111.             if(drew_X)
  1112.               draw[status_col] = save_char;
  1113.  
  1114.             if(sel && !drew_X)
  1115.               EndBold();
  1116.  
  1117.             if(cur)
  1118.               EndInverse();
  1119.         }
  1120.         }
  1121.  
  1122.         screen->entry_state[i].hilite = cur;
  1123.         screen->entry_state[i].bolded = sel;
  1124.         screen->entry_state[i].id     = h->id;
  1125.  
  1126.         if(cur && retval < 0L)
  1127.           retval = i + HEADER_ROWS(state);
  1128.     }
  1129.  
  1130.     /*--- increment n ---*/
  1131.     while(++n <= mn_get_total(screen->msgmap)
  1132.           && get_lflag(screen->stream, screen->msgmap, n, MN_HIDE))
  1133.       ;
  1134.  
  1135.     }
  1136.  
  1137. #ifdef _WINDOWS
  1138.     mswin_endupdate();
  1139. #endif
  1140.     fflush(stdout);
  1141.     return(retval);
  1142. }
  1143.  
  1144.  
  1145.  
  1146. /*----------------------------------------------------------------------
  1147.      Scroll to specified postion.
  1148.  
  1149.  
  1150.   Args: paint - TRUE when this function shoult repaint the screen.
  1151.     pos - position to scroll to.
  1152.  
  1153.   Returns: TRUE - did the scroll operation.
  1154.        FALSE - was not able to do the scroll operation.
  1155.  ----*/
  1156. int
  1157. index_scroll_to_pos (pos)
  1158. long    pos;
  1159. {
  1160.     static short bad_timing = 0;
  1161.     long    i, j, k;
  1162.     
  1163.     if(bad_timing)
  1164.       return (FALSE);
  1165.  
  1166.     /*
  1167.      * Put the requested line at the top of the screen...
  1168.      */
  1169. #if 1
  1170.     /*
  1171.      * Starting at msg 'pos' find next visable message.
  1172.      */
  1173.     for(i=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
  1174.       if(!get_lflag(current_index_state->stream, 
  1175.                 current_index_state->msgmap, i, MN_HIDE)){
  1176.       current_index_state->msg_at_top = i;
  1177.       break;
  1178.       }
  1179.     }
  1180. #else
  1181.     for(i=1L, j=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
  1182.       if(!get_lflag(current_index_state->stream, 
  1183.                 current_index_state->msgmap, i, MN_HIDE)){
  1184.       if((current_index_state->msg_at_top = i) > 
  1185.           mn_get_cur(current_index_state->msgmap))
  1186.         mn_set_cur(current_index_state->msgmap, i);
  1187.  
  1188.       if(--j <= 0L)
  1189.         break;
  1190.       }
  1191.     }
  1192. #endif
  1193.  
  1194.     /*
  1195.      * If single selection, move selected message to be on the sceen.
  1196.      */
  1197.     if (mn_total_cur(current_index_state->msgmap) == 1L) {
  1198.       if (current_index_state->msg_at_top > 
  1199.                   mn_get_cur (current_index_state->msgmap)) {
  1200.     /* Selection was above screen, move to top of screen. */
  1201.     mn_set_cur (current_index_state->msgmap, 
  1202.                     current_index_state->msg_at_top);
  1203.       }
  1204.       else {
  1205.     /* Scan through the screen.  If selection found, leave where is.
  1206.      * Otherwise, move to end of screen */
  1207.         for(  i = current_index_state->msg_at_top, 
  1208.             j = current_index_state->lines_per_page;
  1209.           i != mn_get_cur(current_index_state->msgmap) && 
  1210.         i <= mn_get_total(current_index_state->msgmap) && 
  1211.         j > 0L;
  1212.           i++) {
  1213.         if(!get_lflag(current_index_state->stream, 
  1214.                 current_index_state->msgmap, i, MN_HIDE)){
  1215.             j--;
  1216.             k = i;
  1217.             }
  1218.         }
  1219.     if(j <= 0L)
  1220.         /* Move to end of screen. */
  1221.         mn_set_cur(current_index_state->msgmap, k);
  1222.       }
  1223.     }
  1224.  
  1225.     bad_timing = 0;
  1226.     return (TRUE);
  1227. }
  1228.  
  1229.  
  1230.  
  1231. /*----------------------------------------------------------------------
  1232.      Adjust the index display state down a line, and repaint.
  1233.  
  1234.  
  1235.   Returns: TRUE - did the scroll operation.
  1236.        FALSE - was not able to do the scroll operation.
  1237.  ----*/
  1238. int
  1239. index_scroll_down(scroll_count)
  1240.     long scroll_count;
  1241. {
  1242.     static short bad_timing = 0;
  1243.     long i, j, k;
  1244.     long cur, total;
  1245.  
  1246.     if(bad_timing)
  1247.       return (FALSE);
  1248.  
  1249.     bad_timing = 1;
  1250.     
  1251.     
  1252.     j = -1L;
  1253.     total = mn_get_total (current_index_state->msgmap);
  1254.     for(k = i = current_index_state->msg_at_top; ; i++){
  1255.  
  1256.     /* Only examine non-hidden messages. */
  1257.         if(!get_lflag(current_index_state->stream, 
  1258.               current_index_state->msgmap, i, MN_HIDE)){
  1259.         /* Remember this message */
  1260.         k = i;
  1261.         /* Increment count of lines.  */
  1262.         if (++j >= scroll_count) {
  1263.         /* Counted enough lines, stop. */
  1264.         current_index_state->msg_at_top = k;
  1265.         break;
  1266.         }
  1267.     }
  1268.         
  1269.     /* If at last message, stop. */
  1270.     if (i >= total){
  1271.         current_index_state->msg_at_top = k;
  1272.         break;
  1273.     }
  1274.     }
  1275.  
  1276.     /*
  1277.      * If not multiple selection, see if selected message visable.  if not
  1278.      * set it to last visable message. 
  1279.      */
  1280.     if(mn_total_cur(current_index_state->msgmap) == 1L) {
  1281.     j = 0L;
  1282.     cur = mn_get_cur (current_index_state->msgmap);
  1283.     for (i = current_index_state->msg_at_top; i <= total; ++i) {
  1284.         if(!get_lflag(current_index_state->stream, 
  1285.                   current_index_state->msgmap, i, MN_HIDE)) {
  1286.             if (++j >= current_index_state->lines_per_page) {
  1287.             break;
  1288.             }
  1289.         if (i == cur) 
  1290.             break;
  1291.         }
  1292.         }
  1293.     if (i != cur) 
  1294.         mn_set_cur(current_index_state->msgmap, 
  1295.                         current_index_state->msg_at_top);
  1296.     }
  1297.  
  1298.     bad_timing = 0;
  1299.     return (TRUE);
  1300. }
  1301.  
  1302.  
  1303.  
  1304. /*----------------------------------------------------------------------
  1305.      Adjust the index display state up a line
  1306.  
  1307.   Args: paint - TRUE when this function shoult repaint the screen.
  1308.  
  1309.   Returns: TRUE - did the scroll operation.
  1310.        FALSE - was not able to do the scroll operation.
  1311.  
  1312.  ----*/
  1313. int
  1314. index_scroll_up(scroll_count)
  1315.     long scroll_count;
  1316. {
  1317.     static short bad_timing = 0;
  1318.     long i, j, k;
  1319.     long cur;
  1320.  
  1321.     if(bad_timing)
  1322.       return(FALSE);
  1323.  
  1324.     bad_timing = 1;
  1325.     
  1326.     j = -1L;
  1327.     for(k = i = current_index_state->msg_at_top; ; i--){
  1328.  
  1329.     /* Only examine non-hidden messages. */
  1330.         if(!get_lflag(current_index_state->stream, 
  1331.               current_index_state->msgmap, i, MN_HIDE)){
  1332.         /* Remember this message */
  1333.         k = i;
  1334.         /* Increment count of lines.  */
  1335.         if (++j >= scroll_count) {
  1336.         /* Counted enough lines, stop. */
  1337.         current_index_state->msg_at_top = k;
  1338.         break;
  1339.         }
  1340.     }
  1341.         
  1342.     /* If at first message, stop */
  1343.     if (i <= 1L){
  1344.         current_index_state->msg_at_top = k;
  1345.         break;
  1346.     }
  1347.     }
  1348.  
  1349.     
  1350.     /*
  1351.      * If not multiple selection, see if selected message visable.  if not
  1352.      * set it to last visable message. 
  1353.      */
  1354.     if(mn_total_cur(current_index_state->msgmap) == 1L) {
  1355.     j = 0L;
  1356.     cur = mn_get_cur (current_index_state->msgmap);
  1357.     for (    i = current_index_state->msg_at_top; 
  1358.         i <= mn_get_total(current_index_state->msgmap);
  1359.         ++i) {
  1360.         if(!get_lflag(current_index_state->stream, 
  1361.                   current_index_state->msgmap, i, MN_HIDE)) {
  1362.             if (++j >= current_index_state->lines_per_page) {
  1363.             k = i;
  1364.             break;
  1365.             }
  1366.         if (i == cur) 
  1367.             break;
  1368.         }
  1369.         }
  1370.     if (i != cur) 
  1371.         mn_set_cur(current_index_state->msgmap, k);
  1372.     }
  1373.  
  1374.  
  1375.     bad_timing = 0;
  1376.     return (TRUE);
  1377. }
  1378.  
  1379.  
  1380.  
  1381. /*----------------------------------------------------------------------
  1382.      Calculate the message number that should be at the top of the display
  1383.  
  1384.   Args: current - the current message number
  1385.         lines_per_page - the number of lines for the body of the index only
  1386.  
  1387.   Returns: -1 if the current message is -1 
  1388.            the message entry for the first message at the top of the screen.
  1389.  
  1390. When paging in the index it is always on even page boundies, and the
  1391. current message is always on the page thus the top of the page is
  1392. completely determined by the current message and the number of lines
  1393. on the page. 
  1394.  ----*/
  1395. long
  1396. top_ent_calc(stream, msgs, at_top, lines_per_page)
  1397.      MAILSTREAM *stream;
  1398.      MSGNO_S *msgs;
  1399.      long     at_top, lines_per_page;
  1400. {
  1401.     long current;
  1402.  
  1403.     current = (mn_total_cur(msgs) <= 1L) ? mn_get_cur(msgs) : at_top;
  1404.  
  1405.     if(current < 0L)
  1406.       return(-1);
  1407.  
  1408.     if(lines_per_page == 0L)
  1409.       return(current);
  1410.  
  1411.     if(any_lflagged(msgs, (MN_HIDE|MN_EXLD))){
  1412.     long n, m = 0L, t = 1L;
  1413.  
  1414.     for(n = 1L; n <= mn_get_total(msgs); n++)
  1415.       if(!get_lflag(stream, msgs, n, MN_HIDE)
  1416.          && (++m % lines_per_page) == 1L){
  1417.           if(n > current)
  1418.         break;
  1419.  
  1420.           t = n;
  1421.       }
  1422.  
  1423.     return(t);
  1424.     }
  1425.     else
  1426.       return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
  1427. }
  1428.  
  1429.  
  1430. /*----------------------------------------------------------------------
  1431.       Initialize the index_disp_format array in ps_global from this
  1432.       format string.
  1433.  
  1434.    Args: format -- the string containing the format tokens
  1435.      answer -- put the answer here, free first if there was a previous
  1436.             value here
  1437.  ----*/
  1438. void
  1439. init_index_format(format, answer)
  1440. char         *format;
  1441. INDEX_COL_S **answer;
  1442. {
  1443.     int column = 0;
  1444.  
  1445.     set_need_format_setup();
  1446.     /* if custom format is specified, try it, else go with default */
  1447.     if(!(format && *format && parse_index_format(format, answer))){
  1448.     if(*answer)
  1449.       fs_give((void **)answer);
  1450.  
  1451.     *answer = (INDEX_COL_S *)fs_get(7*sizeof(INDEX_COL_S));
  1452.     memset((void *)(*answer), 0, 7*sizeof(INDEX_COL_S));
  1453.  
  1454.     (*answer)[column].ctype        = iStatus;
  1455.     (*answer)[column].wtype        = Fixed;
  1456.     (*answer)[column++].req_width    = 3;
  1457.  
  1458.     /*
  1459.      * WeCalculate with a non-zero req_width means that this space
  1460.      * will be reserved before calculating the percentages.  It may
  1461.      * get increased or decreased later when we see what we actually need.
  1462.      */
  1463.     (*answer)[column].ctype        = iMessNo;
  1464.     (*answer)[column++].wtype    = WeCalculate;
  1465.  
  1466.     (*answer)[column].ctype        = iDate;
  1467.     (*answer)[column].wtype        = Fixed;
  1468.     (*answer)[column++].req_width    = 6;
  1469.  
  1470.     (*answer)[column].ctype        = iFromTo;
  1471.     (*answer)[column].wtype        = Percent;
  1472.     (*answer)[column++].req_width    = 33; /* percent of rest */
  1473.  
  1474.     (*answer)[column].ctype        = iSize;
  1475.     (*answer)[column++].wtype    = WeCalculate;
  1476.  
  1477.     (*answer)[column].ctype        = iSubject;
  1478.     (*answer)[column].wtype        = Percent;
  1479.     (*answer)[column++].req_width    = 67;
  1480.  
  1481.     (*answer)[column].ctype        = iNothing;
  1482.     }
  1483.  
  1484.     /*
  1485.      * Fill in req_width's for WeCalculate items.
  1486.      */
  1487.     for(column = 0; (*answer)[column].ctype != iNothing; column++){
  1488.     if((*answer)[column].wtype == WeCalculate){
  1489.         switch((*answer)[column].ctype){
  1490.           case iStatus:
  1491.         (*answer)[column].req_width = 3;
  1492.         break;
  1493.           case iFStatus:
  1494.         (*answer)[column].req_width = 6;
  1495.         break;
  1496.           case iMessNo:
  1497.         (*answer)[column].req_width = 3;
  1498.         break;
  1499.           case iDate:
  1500.         (*answer)[column].req_width = 6;
  1501.         break;
  1502.           case iSize:
  1503.         (*answer)[column].req_width = 8;
  1504.         break;
  1505.           case iDescripSize:
  1506.         (*answer)[column].req_width = 9;
  1507.         break;
  1508.         }
  1509.     }
  1510.     }
  1511. }
  1512.  
  1513.  
  1514. struct index_parse_tokens {
  1515.     char        *name;
  1516.     IndexColType ctype;
  1517. };
  1518.  
  1519. struct index_parse_tokens itokens[] = {
  1520.     {"STATUS",      iStatus},
  1521.     {"FULLSTATUS",  iFStatus},
  1522.     {"MSGNO",       iMessNo},
  1523.     {"DATE",        iDate},
  1524.     {"FROMORTO",    iFromTo},
  1525.     {"FROM",        iFrom},
  1526.     {"TO",          iTo},
  1527.     {"SENDER",      iSender},
  1528.     {"SIZE",        iSize},
  1529.     {"DESCRIPSIZE", iDescripSize},
  1530.     {"SUBJECT",     iSubject},
  1531.     {NULL,          iNothing}
  1532. };
  1533.  
  1534. int
  1535. parse_index_format(format_str, answer)
  1536. char         *format_str;
  1537. INDEX_COL_S **answer;
  1538. {
  1539.     int i, column = 0;
  1540.     char *p, *q;
  1541.     struct index_parse_tokens *pt;
  1542.     INDEX_COL_S cdesc[MAXIFLDS]; /* temp storage for answer */
  1543.  
  1544.     get_body_for_index = 0;
  1545.     memset((void *)cdesc, 0, sizeof(cdesc));
  1546.  
  1547.     p = format_str;
  1548.     while(p && *p && column < MAXIFLDS-1){
  1549.     /* skip leading white space for next word */
  1550.     while(p && *p && isspace(*p))
  1551.       p++;
  1552.     
  1553.     /* look for the token this word matches */
  1554.     for(pt = itokens; pt->name; pt++)
  1555.         if(!struncmp(pt->name, p, strlen(pt->name)))
  1556.           break;
  1557.     
  1558.     /* ignore unrecognized word */
  1559.     if(!pt->name){
  1560.         for(q = p; *p && !isspace(*p); p++)
  1561.           ;
  1562.  
  1563.         if(*p)
  1564.           *p++ = '\0';
  1565.  
  1566.         dprint(1, (debugfile,
  1567.                "parse_index_format: unrecognized token: %s\n", q));
  1568.         q_status_message1(SM_ORDER | SM_DING, 0, 3,
  1569.                   "Unrecognized string in index-format: %s", q);
  1570.         continue;
  1571.     }
  1572.  
  1573.     cdesc[column].ctype = pt->ctype;
  1574.     if(pt->ctype == iDescripSize)
  1575.       get_body_for_index = 1;
  1576.  
  1577.     /* skip over name and look for parens */
  1578.     p += strlen(pt->name);
  1579.     if(*p == '('){
  1580.         p++;
  1581.         q = p;
  1582.         while(p && *p && isdigit(*p))
  1583.           p++;
  1584.         
  1585.         if(p && *p && *p == ')' && p > q){
  1586.         cdesc[column].wtype = Fixed;
  1587.         cdesc[column].req_width = atoi(q);
  1588.         }
  1589.         else if(p && *p && *p == '%' && p > q){
  1590.         cdesc[column].wtype = Percent;
  1591.         cdesc[column].req_width = atoi(q);
  1592.         }
  1593.         else{
  1594.         cdesc[column].wtype = WeCalculate;
  1595.         cdesc[column].req_width = 0;
  1596.         }
  1597.     }
  1598.     else{
  1599.         cdesc[column].wtype     = WeCalculate;
  1600.         cdesc[column].req_width = 0;
  1601.     }
  1602.  
  1603.     column++;
  1604.     /* skip text at end of word */
  1605.     while(p && *p && !isspace(*p))
  1606.       p++;
  1607.     }
  1608.  
  1609.     /* if, after all that, we didn't find anything recoznizable, bitch */
  1610.     if(!column){
  1611.     dprint(1, (debugfile, "Completely unrecognizable index-format\n"));
  1612.     q_status_message(SM_ORDER | SM_DING, 0, 3,
  1613.          "Configured \"index-format\" unrecognizable. Using default.");
  1614.     return(0);
  1615.     }
  1616.  
  1617.     /* Finish with Nothing column */
  1618.     cdesc[column].ctype = iNothing;
  1619.  
  1620.     /* free up old answer */
  1621.     if(*answer)
  1622.       fs_give((void **)answer);
  1623.  
  1624.     /* allocate space for new answer */
  1625.     *answer = (INDEX_COL_S *)fs_get((column+1)*sizeof(INDEX_COL_S));
  1626.     memset((void *)(*answer), 0, (column+1)*sizeof(INDEX_COL_S));
  1627.     /* copy answer to real place */
  1628.     for(i = 0; i <= column; i++)
  1629.       (*answer)[i] = cdesc[i];
  1630.  
  1631.     return(1);
  1632. }
  1633.  
  1634.  
  1635. /*----------------------------------------------------------------------
  1636.     This redraws the body of the index screen, taking into
  1637. account any change in the size of the screen. All the state needed to
  1638. repaint is in the static variables so this can be called from
  1639. anywhere.
  1640.  ----*/
  1641. void
  1642. redraw_index_body()
  1643. {
  1644.     ps_global->mangled_body = 1;
  1645.     (void)update_index(ps_global, current_index_state);
  1646. }
  1647.  
  1648.  
  1649.  
  1650. /*----------------------------------------------------------------------
  1651.       Setup the widths of the various columns in the index display
  1652.  
  1653.    Args: news      -- mail stream is news
  1654.      max_msgno -- max message number in mail stream
  1655.  ----*/
  1656. void
  1657. setup_header_widths(cdesc, news, max_msgno)
  1658.     INDEX_COL_S *cdesc;
  1659.     int          news;
  1660.     long         max_msgno;
  1661. {
  1662.     int       i, j, columns, some_to_calculate;
  1663.     int          space_left, screen_width, width, fix, col, scol, altcol;
  1664.     int          keep_going, tot_pct, was_sl;
  1665.     WidthType wtype;
  1666.  
  1667.  
  1668.     dprint(8, (debugfile, "=== setup_header_widths(%d,%ld) ===\n",
  1669.     news, max_msgno));
  1670.  
  1671.     clear_need_format_setup();
  1672.     screen_width = ps_global->ttyo->screen_cols;
  1673.     columns = 0;
  1674.     some_to_calculate = 0;
  1675.     space_left = screen_width;
  1676.  
  1677.     /*
  1678.      * Calculate how many fields there are so we know how many spaces
  1679.      * between columns to reserve.  Fill in Fixed widths now.  Reserve
  1680.      * special case WeCalculate with non-zero req_widths before doing
  1681.      * Percent cases below.
  1682.      */
  1683.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  1684.  
  1685.     /* These aren't included in nr mode */
  1686.     if(ps_global->nr_mode && (cdesc[i].ctype == iFromTo ||
  1687.                   cdesc[i].ctype == iFrom ||
  1688.                   cdesc[i].ctype == iSender ||
  1689.                   cdesc[i].ctype == iTo)){
  1690.         cdesc[i].req_width = 0;
  1691.         cdesc[i].width = 0;
  1692.         cdesc[i].wtype = Fixed;
  1693.     }
  1694.     else if(cdesc[i].wtype == Fixed){
  1695.       cdesc[i].width = cdesc[i].req_width;
  1696.       if(cdesc[i].width > 0)
  1697.         columns++;
  1698.     }
  1699.     else if(cdesc[i].wtype == Percent){
  1700.         cdesc[i].width = 0; /* calculated later */
  1701.         columns++;
  1702.     }
  1703.     else{ /* WeCalculate */
  1704.         cdesc[i].width = cdesc[i].req_width; /* reserve this for now */
  1705.         some_to_calculate++;
  1706.         columns++;
  1707.     }
  1708.  
  1709.     space_left -= cdesc[i].width;
  1710.     }
  1711.  
  1712.     space_left -= (columns - 1); /* space between columns */
  1713.  
  1714.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  1715.     wtype = cdesc[i].wtype;
  1716.     if(wtype != WeCalculate && wtype != Percent && cdesc[i].width == 0)
  1717.       continue;
  1718.  
  1719.     switch(cdesc[i].ctype){
  1720.       case iStatus:
  1721.         cdesc[i].actual_length = 3;
  1722.         cdesc[i].adjustment = Left;
  1723.         break;
  1724.  
  1725.       case iFStatus:
  1726.         cdesc[i].actual_length = 6;
  1727.         cdesc[i].adjustment = Left;
  1728.         break;
  1729.  
  1730.       case iDate:
  1731.         cdesc[i].actual_length = 6;
  1732.         cdesc[i].adjustment = Left;
  1733.         break;
  1734.  
  1735.       case iFromTo:
  1736.       case iFrom:
  1737.       case iSender:
  1738.       case iTo:
  1739.       case iSubject:
  1740.         cdesc[i].adjustment = Left;
  1741.         break;
  1742.  
  1743.       case iMessNo:
  1744.         if(max_msgno < 1000)
  1745.           cdesc[i].actual_length = 3;
  1746.         else if(max_msgno < 10000)
  1747.           cdesc[i].actual_length = 4;
  1748.         else if(max_msgno < 100000)
  1749.           cdesc[i].actual_length = 5;
  1750.         else
  1751.           cdesc[i].actual_length = 6;
  1752.  
  1753.         cdesc[i].adjustment = Right;
  1754.         break;
  1755.  
  1756.       case iSize:
  1757.         if(news)
  1758.           cdesc[i].actual_length = 0;
  1759.         else
  1760.           cdesc[i].actual_length = 8;
  1761.  
  1762.         cdesc[i].adjustment = Right;
  1763.         break;
  1764.  
  1765.       case iDescripSize:
  1766.         if(news)
  1767.           cdesc[i].actual_length = 0;
  1768.         else
  1769.           cdesc[i].actual_length = 9;
  1770.  
  1771.         cdesc[i].adjustment = Right;
  1772.         break;
  1773.     }
  1774.     }
  1775.  
  1776.     /* if have reserved unneeded space for size, give it back */
  1777.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  1778.       if(cdesc[i].ctype == iSize || cdesc[i].ctype == iDescripSize){
  1779.     if(cdesc[i].actual_length == 0){
  1780.       if((fix=cdesc[i].width) > 0){ /* had this reserved */
  1781.         cdesc[i].width = 0;
  1782.         space_left += fix;
  1783.       }
  1784.  
  1785.       space_left++;  /* +1 for space between columns */
  1786.     }
  1787.       }
  1788.     }
  1789.  
  1790.     /*
  1791.      * Calculate the field widths that are basically fixed in width.
  1792.      * Do them in this order in case we don't have enough space to go around.
  1793.      */
  1794.     for(j = 0; j < 6 && space_left > 0 && some_to_calculate; j++){
  1795.       IndexColType targetctype;
  1796.  
  1797.       switch(j){
  1798.     case 0:
  1799.       targetctype = iMessNo;
  1800.       break;
  1801.  
  1802.     case 1:
  1803.       targetctype = iStatus;
  1804.       break;
  1805.  
  1806.     case 2:
  1807.       targetctype = iFStatus;
  1808.       break;
  1809.  
  1810.     case 3:
  1811.       targetctype = iDate;
  1812.       break;
  1813.  
  1814.     case 4:
  1815.       targetctype = iSize;
  1816.       break;
  1817.  
  1818.     case 5:
  1819.       targetctype = iDescripSize;
  1820.       break;
  1821.       }
  1822.  
  1823.       for(i = 0;
  1824.       cdesc[i].ctype != iNothing && space_left >0 && some_to_calculate;
  1825.       i++){
  1826.     if(cdesc[i].ctype == targetctype && cdesc[i].wtype == WeCalculate){
  1827.       some_to_calculate--;
  1828.       fix = min(cdesc[i].actual_length - cdesc[i].width, space_left);
  1829.       cdesc[i].width += fix;
  1830.       space_left -= fix;
  1831.     }
  1832.       }
  1833.     }
  1834.  
  1835.     /*
  1836.      * Fill in widths for Percent cases.  If there are no more to calculate,
  1837.      * use the percentages as relative numbers and use the rest of the space,
  1838.      * else treat them as absolute percentages of the original avail screen.
  1839.      */
  1840.     if(space_left > 0){
  1841.       if(some_to_calculate){
  1842.         for(i = 0; cdesc[i].ctype != iNothing && space_left > 0; i++){
  1843.         if(cdesc[i].wtype == Percent){
  1844.         /* The 2, 200, and +100 are because we're rounding */
  1845.         fix = ((2*cdesc[i].req_width *
  1846.             (screen_width-(columns-1)))+100) / 200;
  1847.             fix = min(fix, space_left);
  1848.             cdesc[i].width += fix;
  1849.             space_left -= fix;
  1850.         }
  1851.     }
  1852.       }
  1853.       else{
  1854.     tot_pct = 0;
  1855.     was_sl = space_left;
  1856.     /* add up total percentages requested */
  1857.         for(i = 0; cdesc[i].ctype != iNothing; i++)
  1858.         if(cdesc[i].wtype == Percent)
  1859.           tot_pct += cdesc[i].req_width;
  1860.  
  1861.     /* give relative weight to requests */
  1862.         for(i = 0;
  1863.         cdesc[i].ctype != iNothing && space_left > 0 && tot_pct > 0;
  1864.         i++){
  1865.         if(cdesc[i].wtype == Percent){
  1866.         fix = ((2*cdesc[i].req_width*was_sl)+tot_pct) / (2*tot_pct);
  1867.             fix = min(fix, space_left);
  1868.             cdesc[i].width += fix;
  1869.             space_left -= fix;
  1870.         }
  1871.     }
  1872.       }
  1873.     }
  1874.  
  1875.     /* split up rest, give twice as much to Subject */
  1876.     keep_going = 1;
  1877.     while(space_left > 0 && keep_going){
  1878.       keep_going = 0;
  1879.       for(i = 0; cdesc[i].ctype != iNothing && space_left > 0; i++){
  1880.     if(cdesc[i].wtype == WeCalculate &&
  1881.       (cdesc[i].ctype == iFromTo ||
  1882.        cdesc[i].ctype == iFrom ||
  1883.        cdesc[i].ctype == iSender ||
  1884.        cdesc[i].ctype == iTo ||
  1885.        cdesc[i].ctype == iSubject)){
  1886.       keep_going++;
  1887.       cdesc[i].width++;
  1888.       space_left--;
  1889.       if(space_left > 0 && cdesc[i].ctype == iSubject){
  1890.           cdesc[i].width++;
  1891.           space_left--;
  1892.       }
  1893.     }
  1894.       }
  1895.     }
  1896.  
  1897.     /* if still more, pad out percent's */
  1898.     keep_going = 1;
  1899.     while(space_left > 0 && keep_going){
  1900.       keep_going = 0;
  1901.       for(i = 0; cdesc[i].ctype != iNothing && space_left > 0; i++){
  1902.     if(cdesc[i].wtype == Percent &&
  1903.       (cdesc[i].ctype == iFromTo ||
  1904.        cdesc[i].ctype == iFrom ||
  1905.        cdesc[i].ctype == iSender ||
  1906.        cdesc[i].ctype == iTo ||
  1907.        cdesc[i].ctype == iSubject)){
  1908.       keep_going++;
  1909.       cdesc[i].width++;
  1910.       space_left--;
  1911.     }
  1912.       }
  1913.     }
  1914.  
  1915.     col = 0;
  1916.     scol = -1;
  1917.     altcol = -1;
  1918.     /* figure out what column is start of status field */
  1919.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  1920.     width = cdesc[i].width;
  1921.     if(width == 0)
  1922.       continue;
  1923.  
  1924.     /* space between columns */
  1925.     if(col > 0)
  1926.       col++;
  1927.  
  1928.     if(cdesc[i].ctype == iStatus){
  1929.         scol = col;
  1930.         break;
  1931.     }
  1932.  
  1933.     if(cdesc[i].ctype == iFStatus){
  1934.         scol = col;
  1935.         break;
  1936.     }
  1937.  
  1938.     if(cdesc[i].ctype == iMessNo)
  1939.       altcol = col;
  1940.  
  1941.     col += width;
  1942.     }
  1943.  
  1944.     if(scol == -1){
  1945.     if(altcol == -1)
  1946.       scol = 0;
  1947.     else
  1948.       scol = altcol;
  1949.     }
  1950.  
  1951.     if(current_index_state)
  1952.       current_index_state->status_col = scol;
  1953. }
  1954.  
  1955.  
  1956.  
  1957. /*----------------------------------------------------------------------
  1958.       Create a string summarizing the message header for index on screen
  1959.  
  1960.    Args: stream -- mail stream to fetch envelope info from
  1961.      msgmap -- message number to pine sort mapping
  1962.      msgno  -- Message number to create line for
  1963.  
  1964.   Result: returns a malloced string
  1965.           saves string in a cache for next call for same header
  1966.  ----*/
  1967. HLINE_S *
  1968. build_header_line(state, stream, msgmap, msgno)
  1969.     struct pine *state;
  1970.     MAILSTREAM  *stream;
  1971.     MSGNO_S     *msgmap;
  1972.     long         msgno;
  1973. {
  1974.     ENVELOPE     *envelope;
  1975.     MESSAGECACHE *cache;
  1976.     ADDRESS      *addr;
  1977.     char          str_buf[MAXIFLDS][MAX_SCREEN_COLS+1], to_us, *field;
  1978.     char         *s_tmp, *buffer, *p, *str;
  1979.     HLINE_S      *hline;
  1980.     struct date   d;
  1981.     INDEX_COL_S  *cdesc;
  1982.     int           i, width, which_array, no_data = 0;
  1983.     BODY *body = NULL, **bodyp;
  1984.  
  1985.  
  1986.     dprint(8, (debugfile, "=== build_header_line(%ld) ===\n", msgno));
  1987.  
  1988.     if(check_need_format_setup())
  1989.       setup_header_widths(state->index_disp_format, IS_NEWS(stream),
  1990.               mn_get_total(msgmap));
  1991.  
  1992.     /* cache hit */
  1993.     if(*(buffer = (hline = get_index_cache(msgno))->line) != '\0') {
  1994.         dprint(9, (debugfile, "Hit: Returning %p -> <%s (%d), %ld>\n",
  1995.            hline, buffer, strlen(buffer), hline->id));
  1996.     return(hline);
  1997.     }
  1998.  
  1999.     bodyp = (get_body_for_index && !IS_NEWS(stream)) ? &body : NULL;
  2000.  
  2001.     envelope = mail_fetchstructure(stream, mn_m2raw(msgmap, msgno), bodyp);
  2002.     cache    = mail_elt(stream, mn_m2raw(msgmap, msgno));
  2003.  
  2004.     if(!envelope || !cache)
  2005.       no_data = 2;
  2006.     /*
  2007.      * Check that the envelope returned has something to display.
  2008.      * If empty, indicate that no message info found.
  2009.      */
  2010.     else if(!envelope->remail && !envelope->return_path && !envelope->date &&
  2011.        !envelope->from && !envelope->sender && !envelope->reply_to &&
  2012.        !envelope->subject && !envelope->to && !envelope->cc &&
  2013.        !envelope->bcc && !envelope->in_reply_to && !envelope->message_id &&
  2014.        !envelope->newsgroups)
  2015.       no_data = 1;
  2016.  
  2017.     cdesc = state->index_disp_format;
  2018.     which_array = 0;
  2019.  
  2020.     /* calculate contents of the required fields */
  2021.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  2022.     width  = cdesc[i].width;
  2023.     if(width == 0)
  2024.       continue;
  2025.  
  2026.     str             = str_buf[which_array++];
  2027.     str[0]          = '\0';
  2028.     cdesc[i].string = str;
  2029.     if(no_data){
  2030.         if(cdesc[i].ctype == iMessNo)
  2031.           sprintf(str, "%ld", msgno);
  2032.         else if(no_data < 2 && cdesc[i].ctype == iSubject)
  2033.           sprintf(str, "%-*.*s", width, width,
  2034.               "[ No Message Text Available ]");
  2035.     }
  2036.     else{
  2037.  
  2038.         switch(cdesc[i].ctype){
  2039.           case iStatus:
  2040.         to_us = (cache->flagged) ? '*' : ' ';
  2041.         for(addr = envelope->to; addr && to_us == ' '; addr=addr->next)
  2042.           if(address_is_us(addr, ps_global))
  2043.             to_us = '+';
  2044.  
  2045. #ifdef    LATER
  2046.     if(to_us == ' '){                /* look for reset-to */
  2047.     char    *fields[2], *resent, *p;
  2048.     ADDRESS *resent_addr = NULL;
  2049.  
  2050.     fields[0] = "Resent-To";
  2051.     fields[1] = NULL;
  2052.     resent = xmail_fetchheader_lines(stream,mn_m2raw(msgmap,msgno),fields);
  2053.     if(resent){
  2054.         if(p = strchr(resent, ':')){
  2055.         for(++p; *p && isspace(*p); p++)
  2056.           ;
  2057.  
  2058.         removing_trailing_white_space(p);
  2059.         rfc822_parse_adrlist(&resent_addr, p, ps_global->maildomain);
  2060.         for(addr = resent_addr; addr && to_us == ' '; addr=addr->next)
  2061.           if(address_is_us(addr, ps_global))
  2062.             to_us = '+';
  2063.  
  2064.         mail_free_address(&resent_addr);
  2065.         }
  2066.  
  2067.         fs_give((void **)&resent);
  2068.     }
  2069.     }
  2070. #endif
  2071.  
  2072.         sprintf(str, "%c %s", to_us, status_string(stream, cache));
  2073.         break;
  2074.  
  2075.           case iFStatus:
  2076.            {char new, answered, deleted, flagged;
  2077.  
  2078.         to_us = ' ';
  2079.         for(addr = envelope->to; addr && to_us == ' '; addr=addr->next)
  2080.           if(address_is_us(addr, ps_global))
  2081.             to_us = '+';
  2082.         new = answered = deleted = flagged = ' ';
  2083.         if(cache && !ps_global->nr_mode){
  2084.             if(!cache->seen &&
  2085.               (!stream
  2086.                || !IS_NEWS(stream)
  2087.                || (cache->recent&&F_ON(F_FAKE_NEW_IN_NEWS,ps_global))))
  2088.               new = 'N';
  2089.  
  2090.             if(cache->answered)
  2091.               answered = 'A';
  2092.  
  2093.             if(cache->deleted)
  2094.               deleted = 'D';
  2095.  
  2096.             if(cache->flagged)
  2097.               flagged = '*';
  2098.         }
  2099.  
  2100.         sprintf(str, "%c %c%c%c%c", to_us,
  2101.             flagged, new, answered, deleted);
  2102.            }
  2103.         break;
  2104.  
  2105.           case iMessNo:
  2106.         sprintf(str, "%ld", msgno);
  2107.         break;
  2108.  
  2109.           case iDate:
  2110.         parse_date(envelope->date, &d);
  2111.         sprintf(str, "%s %2d", month_abbrev(d.month), d.day);
  2112.         break;
  2113.  
  2114.           case iFromTo:
  2115.         addr = envelope->to ? envelope->to
  2116.                      : envelope->cc ? envelope->cc : NULL;
  2117.         field = envelope->to ? "To" : envelope->cc ? "Cc" : NULL;
  2118.         if(addr && (!envelope->from ||
  2119.                address_is_us(envelope->from, ps_global))){
  2120.  
  2121.             if(width > 4)
  2122.               set_index_addr(stream, mn_m2raw(msgmap, msgno),
  2123.                      field, addr, "To: ", width - 4, str);
  2124.             else{
  2125.             strcpy(str, "To: ");
  2126.             str[width] = '\0';
  2127.             }
  2128.             
  2129.             break;
  2130.         }
  2131.  
  2132.         /*
  2133.          * Note: Newsgroups won't work over imap so we won't get
  2134.          * it in that case, and we'll use from.  We don't want to
  2135.          * waste an rtt getting it.
  2136.          */
  2137.         if(!addr && envelope->newsgroups
  2138.                && (!envelope->from
  2139.                || address_is_us(envelope->from, ps_global))){
  2140.             if(width > 4)
  2141.               sprintf(str, "To: %-*.*s", width-4, width-4,
  2142.                   envelope->newsgroups);
  2143.             else{
  2144.             strcpy(str, "To: ");
  2145.             str[width] = '\0';
  2146.             }
  2147.  
  2148.             break;
  2149.         }
  2150.         /* ELSE fall thru and act like a "From" */
  2151.  
  2152.           case iFrom:
  2153.         set_index_addr(stream, mn_m2raw(msgmap, msgno),
  2154.                    "From", envelope->from, NULL, width, str);
  2155.         break;
  2156.  
  2157.           case iTo:
  2158.         addr = envelope->to ? envelope->to
  2159.                      : envelope->cc ? envelope->cc : NULL;
  2160.         field = envelope->to ? "To" : envelope->cc ? "Cc" : NULL;
  2161.         if(!set_index_addr(stream, mn_m2raw(msgmap, msgno),
  2162.                    field, addr, NULL, width, str)
  2163.            && envelope->newsgroups)
  2164.           sprintf(str, "%-*.*s", width, width, envelope->newsgroups);
  2165.  
  2166.         break;
  2167.  
  2168.           case iSender:
  2169.         set_index_addr(stream, mn_m2raw(msgmap, msgno),
  2170.                    "Sender", envelope->sender, NULL, width, str);
  2171.         break;
  2172.  
  2173.           case iSize:
  2174.         if(!IS_NEWS(stream)){
  2175.             if(cache->rfc822_size < 100000){
  2176.             s_tmp = comatose(cache->rfc822_size);
  2177.             sprintf(str, "(%s)", s_tmp);
  2178.             }
  2179.             else if(cache->rfc822_size < 10000000){
  2180.             s_tmp = comatose(cache->rfc822_size/1000);
  2181.             sprintf(str, "(%sK)", s_tmp);
  2182.             }
  2183.             else
  2184.               strcpy(str, "(BIG!)");
  2185.         }
  2186.  
  2187.         break;
  2188.  
  2189.           case iDescripSize:
  2190.         if(!IS_NEWS(stream) && body){
  2191.           switch(body->type){
  2192.             case TYPETEXT:
  2193.               if(cache->rfc822_size < 6000)
  2194.             strcpy(str, "(short  )");
  2195.               else if(cache->rfc822_size < 25000)
  2196.             strcpy(str, "(medium )");
  2197.               else if(cache->rfc822_size < 100000)
  2198.             strcpy(str, "(long   )");
  2199.               else
  2200.             strcpy(str, "(huge   )");
  2201.  
  2202.               break;
  2203.  
  2204.             case TYPEMULTIPART:
  2205.               if(strucmp(body->subtype, "MIXED") == 0){
  2206.             switch(body->contents.part->body.type){
  2207.               case TYPETEXT:
  2208.                 if(body->contents.part->body.size.bytes < 6000)
  2209.                   strcpy(str, "(short+ )");
  2210.                 else if(body->contents.part->body.size.bytes < 25000)
  2211.                   strcpy(str, "(medium+)");
  2212.                 else if(body->contents.part->body.size.bytes < 100000)
  2213.                   strcpy(str, "(long+  )");
  2214.                 else
  2215.                   strcpy(str, "(huge+  )");
  2216.                 break;
  2217.  
  2218.               default:
  2219.                 strcpy(str, "(multi  )");
  2220.                 break;
  2221.             }
  2222.               }
  2223.               else if(strucmp(body->subtype, "DIGEST") == 0)
  2224.             strcpy(str, "(digest )");
  2225.               else if(strucmp(body->subtype, "ALTERNATIVE") == 0)
  2226.             strcpy(str, "(mul/alt)");
  2227.               else if(strucmp(body->subtype, "PARALLEL") == 0)
  2228.             strcpy(str, "(mul/par)");
  2229.               else
  2230.                 strcpy(str, "(multi  )");
  2231.  
  2232.               break;
  2233.  
  2234.             case TYPEMESSAGE:
  2235.               strcpy(str, "(message)");
  2236.               break;
  2237.  
  2238.             case TYPEAPPLICATION:
  2239.               strcpy(str, "(applica)");
  2240.               break;
  2241.  
  2242.             case TYPEAUDIO:
  2243.               strcpy(str, "(audio  )");
  2244.               break;
  2245.  
  2246.             case TYPEIMAGE:
  2247.               strcpy(str, "(image  )");
  2248.               break;
  2249.  
  2250.             case TYPEVIDEO:
  2251.               strcpy(str, "(video  )");
  2252.               break;
  2253.  
  2254.             default:
  2255.               strcpy(str, "(other  )");
  2256.               break;
  2257.           }
  2258.         }
  2259.         break;
  2260.  
  2261.           case iSubject:
  2262.         p = str;
  2263.         if(ps_global->nr_mode){
  2264.             str[0] = ' ';
  2265.             str[1] = '\0';
  2266.             p++;
  2267.             width--;
  2268.         }
  2269.  
  2270.         if(envelope->subject)
  2271.           sprintf(p, "%.*s", width,
  2272.               rfc1522_decode((unsigned char *)tmp_20k_buf,
  2273.                      envelope->subject, NULL));
  2274.  
  2275.         break;
  2276.         }
  2277.     }
  2278.     }
  2279.  
  2280.     *buffer = '\0';
  2281.     p = buffer;
  2282.     /*--- Put them all together ---*/
  2283.     for(i = 0; cdesc[i].ctype != iNothing; i++){
  2284.     width = cdesc[i].width;
  2285.     if(width == 0)
  2286.       continue;
  2287.  
  2288.     /* space between columns */
  2289.     if(p > buffer){
  2290.         *p++ = ' ';
  2291.         *p = '\0';
  2292.     }
  2293.  
  2294.     if(cdesc[i].adjustment == Left)
  2295.       sprintf(p, "%-*.*s", width, width, cdesc[i].string);
  2296.     else
  2297.       sprintf(p, "%*.*s", width, width, cdesc[i].string);
  2298.     
  2299.     p += width;
  2300.     }
  2301.  
  2302.     /* Truncate it to be sure not too wide */
  2303.     buffer[min(ps_global->ttyo->screen_cols, i_cache_width())] = '\0';
  2304.     hline->id = line_hash(buffer);
  2305.     dprint(9, (debugfile, "Returning %p -> <%s (%d), %ld>\n",
  2306.            hline, buffer, strlen(buffer), hline->id));
  2307.     return(hline);
  2308. }
  2309.  
  2310.  
  2311. int
  2312. set_index_addr(stream, msgno, field, addr, prefix, width, s)
  2313.     MAILSTREAM *stream;
  2314.     long    msgno;
  2315.     char       *field;
  2316.     ADDRESS    *addr;
  2317.     char       *prefix;
  2318.     int        width;
  2319.     char       *s;
  2320. {
  2321.     ADDRESS *atmp;
  2322.  
  2323.     for(atmp = addr; stream && atmp; atmp = atmp->next)
  2324.       if(atmp->host && atmp->host[0] == '.'){
  2325.       char *p, *h, *fields[2];
  2326.  
  2327.       fields[0] = field;
  2328.       fields[1] = NULL;
  2329.       if(h = xmail_fetchheader_lines(stream, msgno, fields)){
  2330.           /* skip "field:" */
  2331.           for(p = h + strlen(field) + 1; *p && isspace(*p); p++)
  2332.         ;
  2333.  
  2334.           while(width--)
  2335.         if(*p == '\015' || *p == '\012')
  2336.           p++;                /* skip CR LF */
  2337.         else if(!*p)
  2338.           *s++ = ' ';
  2339.         else
  2340.           *s++ = *p++;
  2341.  
  2342.           *s = '\0';            /* tie off return string */
  2343.           fs_give((void **) &h);
  2344.           return(TRUE);
  2345.       }
  2346.       /* else fall thru and display what c-client gave us */
  2347.       }
  2348.  
  2349.     if(addr && !addr->next        /* only one address */
  2350.        && addr->host            /* not group syntax */
  2351.        && addr->personal){        /* there is a personal name */
  2352.     char *dummy = NULL;
  2353.  
  2354.     sprintf(s, "%s%.*s", prefix ? prefix : "", width,
  2355.         rfc1522_decode((unsigned char *)tmp_20k_buf,
  2356.                    addr->personal, &dummy));
  2357.     if(dummy)
  2358.       fs_give((void **)&dummy);
  2359.  
  2360.     return(TRUE);
  2361.     }
  2362.     else if(addr){
  2363.     char *a_string;
  2364.  
  2365.     a_string = addr_list_string(addr, NULL, 0);
  2366.     sprintf(s, "%s%-*.*s", prefix ? prefix : "", width, width, a_string);
  2367.     fs_give((void **)&a_string);
  2368.     return(TRUE);
  2369.     }
  2370.  
  2371.     return(FALSE);
  2372. }
  2373.  
  2374.  
  2375.  
  2376. long
  2377. line_hash(s)
  2378.      char *s;
  2379. {
  2380.     register long xsum = 0L;
  2381.  
  2382.     while(*s)
  2383.       xsum = ((((xsum << 4) & 0xffffffff) + (xsum >> 24)) & 0x0fffffff) + *s++;
  2384.  
  2385.     return(xsum ? xsum : 1L);
  2386. }
  2387.  
  2388.  
  2389. /*-----
  2390.   The following are used to report on sorting progress to the user
  2391.  
  2392. This code is not ideal as it varies depending on how the mail driver
  2393. performs with respect to fetching envelopes.  For some c-client drivers
  2394. the initial fetch of the envelope is expensive and it is cached after
  2395. that. For others every fetch is expensive.  The code here deals with
  2396. with the case where only the first fetch is expensive. It reports
  2397. progress based on how many messages have been fetched, assuming the sort
  2398. will complete very rapidly when all the messages have been fetched. 
  2399.  
  2400. There is a bitmap that keeps track of which messages have been
  2401. fetched that is used to keep an accurate count of the number of messages
  2402. that have been fetched. The sort algorithm fetches envelopes in a random
  2403. order and possibly many times. 
  2404.  
  2405. For c-client drivers for which every fetch envelope is expensive, this
  2406. algorithm will report that the sort is 100% complete once all the envelopes
  2407. have been fetched at least once.
  2408.   ----*/
  2409.  
  2410. #ifndef DOS
  2411. /*
  2412.  * Bitmap for keeping track of messages fetched so progress of
  2413.  * sort can be reported 
  2414.  */
  2415. static unsigned short *fetched_map;
  2416. static long           fetched_count;
  2417.  
  2418. #define  GET_FETCHED_MAP(x)  (fetched_map[(x-1)/16] & (0x1 << ((x-1) & 0xf)))
  2419. #define  SET_FETCHED_MAP(x)  (fetched_map[(x-1)/16] |= (0x1 << ((x-1) & 0xf)))
  2420.  
  2421.  
  2422. /*
  2423.  * Return value for use by progress bar.
  2424.  */
  2425. int
  2426. percent_sorted()
  2427. {
  2428.     return((fetched_count*100) / mn_get_total(ps_global->msgmap));
  2429. }
  2430. #endif    /* !DOS */
  2431.  
  2432.  
  2433. /*----------------------------------------------------------------------
  2434.   Compare function for sorting on subjects. Ignores case, space and "re:"
  2435.   ----*/
  2436. int
  2437. compare_subjects(a, b)
  2438.     const QSType *a, *b;
  2439. {
  2440.     char *suba, *subb;
  2441.     long *mess_a = (long *)a, *mess_b = (long *)b;
  2442.     int   diff, res;
  2443.     long  mdiff;
  2444.  
  2445.     if(ps_global->intr_pending)
  2446.       longjmp(jump_past_qsort, 1);
  2447.  
  2448.     suba = get_sub(*mess_a);
  2449.     subb = get_sub(*mess_b);
  2450.  
  2451.     diff = strucmp(suba, subb);
  2452.  
  2453.     if(diff == 0)
  2454.       mdiff = *mess_a - *mess_b;
  2455.  
  2456.     /* convert to int */
  2457.     res = diff != 0 ? diff :
  2458.            mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2459.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2460. }
  2461.  
  2462.  
  2463. /*----------------------------------------------------------------------
  2464.    Get the subject of a message suitable for sorting.  This removes
  2465.  all re[*]: or [ from the beginning of a message.
  2466.  ----*/
  2467. char *
  2468. get_sub(mess)
  2469.     long mess;
  2470. {
  2471.     ENVELOPE *e;
  2472.     char *s, *s3, *ss;
  2473.     int   l;
  2474.  
  2475.     if(!(ss = subject_cache_ent(mess))){
  2476.     e = mail_fetchstructure(ps_global->mail_stream, mess, NULL);
  2477.     if(e && e->subject) {
  2478.         /* ---- Clean junk off the front of the subject ----*/
  2479.         for(s = e->subject; *s && (isspace(*s) || *s == '['); s++)
  2480.           /* do nothing */;
  2481.  
  2482.         if((*s == 'R' || *s == 'r') && (*(s+1) == 'E' || *(s+1) == 'e')
  2483.            && (*(s+2) == ':' || *(s+2) == '[')){
  2484.         s += 3;
  2485.         if(*(s+2) == '['){
  2486.             while(*s && *s != ':')
  2487.               s++;
  2488.  
  2489.             s++;
  2490.         }
  2491.         }
  2492.  
  2493.         while(*s && (isspace(*s) || *s == '['))
  2494.           s++;
  2495.  
  2496.         if(*(ss = subject_cache_add(mess, s))){
  2497.         /*----- Now, truncate junk off the back end of the subject---*/
  2498.         for(s3 = NULL, s = ss; *s; s++)    /* blast ws and ']' */
  2499.           s3 = (!isspace(*s) && *s != ']') ? NULL : (!s3) ? s : s3;
  2500.  
  2501.         if(s3)
  2502.           *s3 = '\0';
  2503.  
  2504.         if((l=(s3 ? s3 : s)-ss) > 5 && !strucmp(ss+l-5,"(fwd)"))
  2505.           ss[l-5] = '\0';
  2506.  
  2507.         for(s3 = NULL, s = ss; *s; s++)    /* blast ws and ']' */
  2508.           s3 = (!isspace(*s) && *s != ']') ? NULL : (!s3) ? s : s3;
  2509.  
  2510.         if(s3)
  2511.           *s3 = '\0';
  2512.         }
  2513.     }
  2514.     else
  2515.       ss = subject_cache_add(mess, "");
  2516.  
  2517.     dprint(9, (debugfile, "SUB-GET-%s-GET-SUB\n", ss));
  2518.     }
  2519.     else{
  2520.     dprint(9, (debugfile, "SUB-HIT-%s-HIT-SUB\n", ss));
  2521.     }
  2522.  
  2523. #ifndef DOS
  2524.     if(fetched_map != NULL && !GET_FETCHED_MAP(mess)) {
  2525.         SET_FETCHED_MAP(mess);
  2526.         fetched_count++;
  2527.     }
  2528. #endif
  2529.  
  2530.     ALARM_BLIP();
  2531.  
  2532.     return(ss);
  2533. }
  2534.  
  2535.  
  2536. /*----------------------------------------------------------------------
  2537.    Compare the From: fields for sorting. Ignore case.  Only consider the
  2538.    mailbox portion of the address (part left of @).
  2539.    ----*/
  2540. int 
  2541. compare_from(a, b)
  2542.     const QSType *a, *b;
  2543. {
  2544.     long     *mess_a = (long *)a, *mess_b = (long *)b;
  2545.     ENVELOPE *e;
  2546.     char      froma[200], fromb[200];
  2547.     int       diff, res;
  2548.  
  2549.     if(ps_global->intr_pending)
  2550.       longjmp(jump_past_qsort, 1);
  2551.  
  2552.     e = mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2553.     if(e == NULL || e->from == NULL || e->from->mailbox == NULL)  
  2554.       froma[0] = '\0';
  2555.     else
  2556.       strncpy(froma, e->from->mailbox, sizeof(froma) - 1);
  2557.  
  2558.     froma[sizeof(froma) - 1] = '\0';
  2559.  
  2560.     e = mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2561.     if(e == NULL || e->from == NULL || e->from->mailbox == NULL)  
  2562.       fromb[0] = '\0';
  2563.     else
  2564.       strncpy(fromb, e->from->mailbox, sizeof(fromb) - 1);
  2565.  
  2566.     fromb[sizeof(fromb) - 1] = '\0';
  2567.  
  2568. #ifndef DOS
  2569.     if(!GET_FETCHED_MAP(*mess_a)){
  2570.         SET_FETCHED_MAP(*mess_a);
  2571.         fetched_count++;
  2572.     }
  2573.  
  2574.     if(!GET_FETCHED_MAP(*mess_b)){
  2575.         SET_FETCHED_MAP(*mess_b);
  2576.         fetched_count++;
  2577.     }
  2578. #endif
  2579.  
  2580.     ALARM_BLIP();
  2581.  
  2582.     diff = strucmp(froma, fromb);
  2583.     if(diff == 0){
  2584.     long mdiff;
  2585.  
  2586.         mdiff = *mess_a - *mess_b;  /* arrival order */
  2587.     /* convert to int */
  2588.     res = mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2589.     }
  2590.     else
  2591.       res = diff;
  2592.     
  2593.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2594.  
  2595.  
  2596. /*----------------------------------------------------------------------
  2597.    Compare the To: fields for sorting. Ignore case. Use 1st to.
  2598.    ----*/
  2599. int 
  2600. compare_to(a, b)
  2601.     const QSType *a, *b;
  2602. {
  2603.     long     *mess_a = (long *)a, *mess_b = (long *)b;
  2604.     ENVELOPE *e;
  2605.     char      toa[200], tob[200];
  2606.     int       diff, res;
  2607.  
  2608.     if(ps_global->intr_pending)
  2609.       longjmp(jump_past_qsort, 1);
  2610.  
  2611.     e = mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2612.     if(e == NULL || e->to == NULL || e->to->mailbox == NULL)  
  2613.       toa[0] = '\0';
  2614.     else
  2615.       strncpy(toa, e->to->mailbox, sizeof(toa) - 1);
  2616.  
  2617.     toa[sizeof(toa) - 1] = '\0';
  2618.  
  2619.     e = mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2620.     if(e == NULL || e->to == NULL || e->to->mailbox == NULL)  
  2621.       tob[0] = '\0';
  2622.     else
  2623.       strncpy(tob, e->to->mailbox, sizeof(tob) - 1);
  2624.  
  2625.     tob[sizeof(tob) - 1] = '\0';
  2626.  
  2627. #ifndef DOS
  2628.     if(!GET_FETCHED_MAP(*mess_a)){
  2629.         SET_FETCHED_MAP(*mess_a);
  2630.         fetched_count++;
  2631.     }
  2632.  
  2633.     if(!GET_FETCHED_MAP(*mess_b)){
  2634.         SET_FETCHED_MAP(*mess_b);
  2635.         fetched_count++;
  2636.     }
  2637. #endif
  2638.  
  2639.     ALARM_BLIP();
  2640.  
  2641.     diff = strucmp(toa, tob);
  2642.     if(diff == 0){
  2643.     long mdiff;
  2644.  
  2645.         mdiff = *mess_a - *mess_b;  /* arrival order */
  2646.     /* convert to int */
  2647.     res = mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2648.     }
  2649.     else
  2650.       res = diff;
  2651.     
  2652.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2653.  
  2654.  
  2655. /*----------------------------------------------------------------------
  2656.    Compare the Cc: fields for sorting. Ignore case. Use 1st cc.
  2657.    ----*/
  2658. int 
  2659. compare_cc(a, b)
  2660.     const QSType *a, *b;
  2661. {
  2662.     long     *mess_a = (long *)a, *mess_b = (long *)b;
  2663.     ENVELOPE *e;
  2664.     char      cca[200], ccb[200];
  2665.     int       diff, res;
  2666.  
  2667.     if(ps_global->intr_pending)
  2668.       longjmp(jump_past_qsort, 1);
  2669.  
  2670.     e = mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2671.     if(e == NULL || e->cc == NULL || e->cc->mailbox == NULL)  
  2672.       cca[0] = '\0';
  2673.     else
  2674.       strncpy(cca, e->cc->mailbox, sizeof(cca) - 1);
  2675.  
  2676.     cca[sizeof(cca) - 1] = '\0';
  2677.  
  2678.     e = mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2679.     if(e == NULL || e->cc == NULL || e->cc->mailbox == NULL)  
  2680.       ccb[0] = '\0';
  2681.     else
  2682.       strncpy(ccb, e->cc->mailbox, sizeof(ccb) - 1);
  2683.  
  2684.     ccb[sizeof(ccb) - 1] = '\0';
  2685.  
  2686. #ifndef DOS
  2687.     if(!GET_FETCHED_MAP(*mess_a)){
  2688.         SET_FETCHED_MAP(*mess_a);
  2689.         fetched_count++;
  2690.     }
  2691.  
  2692.     if(!GET_FETCHED_MAP(*mess_b)){
  2693.         SET_FETCHED_MAP(*mess_b);
  2694.         fetched_count++;
  2695.     }
  2696. #endif
  2697.  
  2698.     ALARM_BLIP();
  2699.  
  2700.     diff = strucmp(cca, ccb);
  2701.     if(diff == 0){
  2702.     long mdiff;
  2703.  
  2704.         mdiff = *mess_a - *mess_b;  /* arrival order */
  2705.     /* convert to int */
  2706.     res = mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2707.     }
  2708.     else
  2709.       res = diff;
  2710.     
  2711.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2712.  
  2713.  
  2714. /*----------------------------------------------------------------------
  2715.    Compare dates (not arrival times, but time from Date header)
  2716.   ----*/
  2717. int
  2718. compare_message_dates(a, b)
  2719.     const QSType *a, *b;
  2720. {
  2721.     long        *mess_a = (long *)a, *mess_b = (long *)b;
  2722.     int          diff, res;
  2723.     long         mdiff;
  2724.     ENVELOPE    *e;
  2725.     MESSAGECACHE mc_a, mc_b;
  2726.  
  2727.     if(ps_global->intr_pending)
  2728.       longjmp(jump_past_qsort, 1);
  2729.  
  2730.     mdiff = *mess_a - *mess_b;
  2731.     res = mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2732.  
  2733.     e = mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2734.     if(e == NULL || e->date == NULL)
  2735.       mc_a.valid = 0;
  2736.     else if(!mail_parse_date(&mc_a, e->date))
  2737.       mc_a.valid = 0;
  2738.     else
  2739.       mc_a.valid = 1;  /* borrow the valid bit */
  2740.  
  2741.     e = mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2742.     if(e == NULL || e->date == NULL)
  2743.       mc_b.valid = 0;
  2744.     else if(!mail_parse_date(&mc_b, e->date))
  2745.       mc_b.valid = 0;
  2746.     else
  2747.       mc_b.valid = 1;
  2748.  
  2749.     if(!mc_a.valid){
  2750.     if(mc_b.valid)
  2751.       return -1;
  2752.     else
  2753.       return(res);
  2754.     }
  2755.     else{
  2756.     if(!mc_b.valid)
  2757.       return 1;
  2758.     }
  2759.  
  2760.     diff = compare_dates(&mc_a, &mc_b);
  2761.  
  2762. #ifndef DOS
  2763.     if(!GET_FETCHED_MAP(*mess_a)){
  2764.         SET_FETCHED_MAP(*mess_a);
  2765.         fetched_count++;
  2766.     }
  2767.  
  2768.     if(!GET_FETCHED_MAP(*mess_b)){
  2769.         SET_FETCHED_MAP(*mess_b);
  2770.         fetched_count++;
  2771.     }
  2772. #endif
  2773.  
  2774.     ALARM_BLIP();
  2775.     
  2776.     /* convert to int */
  2777.     res = diff != 0 ? diff : res;
  2778.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2779. }
  2780.  
  2781.     
  2782.  
  2783. /*----------------------------------------------------------------------
  2784.   Compare size of messages for sorting
  2785.  ----*/
  2786. int
  2787. compare_size(a, b)
  2788.     const QSType *a, *b;
  2789. {
  2790.     long      *mess_a = (long *)a, *mess_b = (long *)b;
  2791.     long      size_a, size_b, sdiff, mdiff;
  2792.     MESSAGECACHE *mc;
  2793.     int          res;
  2794.  
  2795.     if(ps_global->intr_pending)
  2796.       longjmp(jump_past_qsort, 1);
  2797.  
  2798.     mail_fetchstructure(ps_global->mail_stream, *mess_a, NULL);
  2799.     mc = mail_elt(ps_global->mail_stream, *mess_a);
  2800.     size_a = mc != NULL ? mc->rfc822_size : -1L;
  2801.  
  2802.     mail_fetchstructure(ps_global->mail_stream, *mess_b, NULL);
  2803.     mc = mail_elt(ps_global->mail_stream, *mess_b);
  2804.     size_b = mc != NULL ? mc->rfc822_size : -1L;
  2805.  
  2806. #ifndef DOS
  2807.     if(!GET_FETCHED_MAP(*mess_a)){
  2808.         SET_FETCHED_MAP(*mess_a);
  2809.         fetched_count++;
  2810.     }
  2811.  
  2812.     if(!GET_FETCHED_MAP(*mess_b)){
  2813.         SET_FETCHED_MAP(*mess_b);
  2814.         fetched_count++;
  2815.     }
  2816. #endif    
  2817.  
  2818.     ALARM_BLIP();
  2819.  
  2820.     sdiff = size_a - size_b;
  2821.     if(sdiff == 0L)
  2822.       mdiff = *mess_a - *mess_b;
  2823.  
  2824.     /* convert to int */
  2825.     res = sdiff != 0L ? (sdiff > 0L ? 1 : -1) :
  2826.            mdiff != 0L ? (mdiff > 0L ? 1 : -1) : 0;
  2827.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2828. }
  2829.  
  2830.  
  2831. /*----------------------------------------------------------------------
  2832.   Compare raw message numbers 
  2833.  ----*/
  2834. int
  2835. compare_arrival(a, b)
  2836.     const QSType *a, *b;
  2837. {
  2838.     long *mess_a = (long *)a, *mess_b = (long *)b, mdiff;
  2839.     int   res;
  2840.  
  2841.     res = (mdiff = *mess_a - *mess_b) ? (mdiff > 0L ? 1 : -1) : 0;
  2842.     return(mn_get_revsort(ps_global->msgmap) ? -res : res);
  2843. }
  2844.  
  2845.  
  2846. #if    defined(DOS) && !defined(_WINDOWS)
  2847. static FILE *second_sort_file;    /* Can't afford an array with DOS */
  2848. static char *second_sort_file_name = NULL;
  2849. #else
  2850. static long *second_sort;    /* Array of message numbers */
  2851. #endif
  2852.  
  2853. /*----------------------------------------------------------------------
  2854.    Each message is in a subject group.  That group is indexed by the
  2855.    Date of its oldest member.  Sort first by that date, and within a
  2856.    single subject group, sort by Date of each message.
  2857.   ----*/
  2858. int
  2859. compare_subject_2(a, b)
  2860.     const QSType *a, *b;
  2861. {
  2862.     long       *mess_a = (long *)a, *mess_b = (long *)b;
  2863.     long        a1, b1;
  2864.     long        diff;
  2865.     int         res;
  2866.  
  2867.     if(ps_global->intr_pending)
  2868.       longjmp(jump_past_qsort, 1);
  2869.  
  2870. #if    defined(DOS) && !defined(_WINDOWS)
  2871.     /* There ought to be a better way than seeking all over 
  2872.        the disk, but too lazy at the moment */
  2873.     fseek(second_sort_file, *mess_a * sizeof(long), 0);
  2874.     fread((void *)&a1, sizeof(a1), (size_t)1, second_sort_file);
  2875.     fseek(second_sort_file, *mess_b * sizeof(long), 0);
  2876.     fread((void *)&b1, sizeof(b1), (size_t)1, second_sort_file);
  2877. #else
  2878.     a1 = second_sort[*mess_a];
  2879.     b1 = second_sort[*mess_b];
  2880. #endif
  2881.     
  2882.     diff = a1 - b1;
  2883.  
  2884.     /*
  2885.      * Note, secondary sort is by Date instead of by arrival time as
  2886.      * with most of the other compare functions.
  2887.      */
  2888.     res = diff != 0L ? (diff > 0L ? 1 : -1) : compare_message_dates(a, b);
  2889.  
  2890.     return((mn_get_revsort(ps_global->msgmap) && diff != 0L) ? -res : res);
  2891. }
  2892.  
  2893.  
  2894.     
  2895. /*----------------------------------------------------------------------
  2896.      Sort messages on subject, grouping subjects by date of oldest member
  2897.  
  2898. Args: sort -- The resulting sorted array of message numbers
  2899.       max_msgno -- The number of messages in this view
  2900.       total_max -- The largest possible message number
  2901.  
  2902. This is the OrderedSubjectSort style of subject sort akin to message threading
  2903. based on subject.  All the subjects are grouped together and then the 
  2904. groups are ordered based on the oldest message in each group. 
  2905.  
  2906. Here the groups of subjects are sorted.  Before calling this function,
  2907. the messages have been sorted by subject.
  2908.  
  2909. The second array is indexed by raw message numbers and contains the
  2910. the date (in seconds) of the oldest message in the same subject group.
  2911.  
  2912. This code could be used to calculate information for true
  2913. message threading.  For example, a data structure that had one entry
  2914. per unique subject.  The data structure would probably only need to contain
  2915. the *sorted* message number of the first message in the thread and the 
  2916. number of messages in the thread. That would be assuming that the sort
  2917. array is sorted in the appropriate order. 
  2918.  
  2919. On DOS where memory is expensive the secondary array is a disk file instead
  2920. of being in memory. (Though for many formats sorting on DOS will be 
  2921. extremely painful until some local caching of sort terms is invented.)
  2922.  ----*/
  2923. void
  2924. second_subject_sort(sort, max_msgno, total_max)
  2925.     long *sort, max_msgno, total_max;
  2926. {
  2927.     char  *sub_group, *sub;
  2928.     long   start_sub_group, m, s, t, min_sub_group_date;
  2929.     long   for_zero_dates = 0L;
  2930.     int    check_again;
  2931.  
  2932.     if(max_msgno < 1 || total_max < 1)
  2933.       return; 
  2934.  
  2935. #if    defined(DOS) && !defined(_WINDOWS)
  2936.     second_sort_file_name = temp_nam(NULL, "sf");
  2937.     second_sort_file      = fopen(second_sort_file_name, "wb+");
  2938. #else
  2939.     second_sort      = (long *)fs_get(sizeof(long) * (size_t)(total_max + 1));
  2940. #endif
  2941.  
  2942.     /* init for loop */
  2943.     sub_group          = get_sub(sort[1]);
  2944.     start_sub_group    = 1;
  2945.     min_sub_group_date = seconds_since_epoch(sort[1]);
  2946.     /*
  2947.      * This is so that unparseable dates won't all be the same.  That would
  2948.      * make it look like they're all in the same subject group.
  2949.      */
  2950.     if(min_sub_group_date == 0){
  2951.     for_zero_dates += 10;
  2952.     min_sub_group_date = for_zero_dates;
  2953.     }
  2954.  
  2955.     /*
  2956.      * Create the second array of subject groups.  The sort array had to be
  2957.      * sorted by subject before this function was called, so that each
  2958.      * subject group would be contiguous in the array.  (Didn't actually have
  2959.      * to be sorted, just contiguous subject groups in any order.)
  2960.      */
  2961.     for(s = 2; s <= max_msgno; s++){
  2962.     sub = get_sub(sort[s]);
  2963.         if(strucmp(sub_group, sub)){ /* new subject */
  2964.         /* record the previous group */
  2965. #if    !defined(DOS) || defined(_WINDOWS)
  2966.         /*
  2967.          * If two different groups both have the same min_sub_group_date,
  2968.          * then they'll get mushed together when we sort.  We want to make
  2969.          * sure that doesn't happen, so we need to check if this date
  2970.          * is already recorded somewhere in second_sort and change it
  2971.          * if it is.
  2972.          *
  2973.          * (Too expensive for DOS, count on it being very rare.)
  2974.          */
  2975.         check_again = 1;
  2976.         while(check_again){
  2977.         check_again = 0;
  2978.         for(m = 1; m < start_sub_group; m++){
  2979.             if(min_sub_group_date == second_sort[sort[m]]){
  2980.             min_sub_group_date++;
  2981.             check_again = 1;
  2982.             break;
  2983.             }
  2984.         }
  2985.         }
  2986. #endif
  2987.             for(t = start_sub_group; t < s; t++){
  2988. #if    defined(DOS) && !defined(_WINDOWS)
  2989.                 fseek(second_sort_file, sort[t] * sizeof(long), 0);
  2990.                 fwrite((void *)&min_sub_group_date,
  2991.                        sizeof(min_sub_group_date),
  2992.                        (size_t)1,  second_sort_file);
  2993. #else
  2994.         second_sort[sort[t]] = min_sub_group_date;
  2995. #endif                
  2996.         }
  2997.  
  2998.         /* reset for next subject */
  2999.             start_sub_group     = s;
  3000.             sub_group           = sub;
  3001.         }
  3002.  
  3003.     if(s > start_sub_group)
  3004.       min_sub_group_date =
  3005.         min(min_sub_group_date, seconds_since_epoch(sort[s]));
  3006.     else /* new subject */
  3007.       min_sub_group_date = seconds_since_epoch(sort[s]);
  3008.  
  3009.     if(min_sub_group_date == 0){
  3010.         for_zero_dates += 10;
  3011.         min_sub_group_date = for_zero_dates;
  3012.     }
  3013.     }
  3014.  
  3015.     /* record final group */
  3016. #if    !defined(DOS) || defined(_WINDOWS)
  3017.     check_again = 1;
  3018.     while(check_again){
  3019.         check_again = 0;
  3020.         for(m = 1; m < start_sub_group; m++){
  3021.         if(min_sub_group_date == second_sort[sort[m]]){
  3022.             min_sub_group_date++;
  3023.             check_again = 1;
  3024.             break;
  3025.         }
  3026.         }
  3027.     }
  3028. #endif
  3029.  
  3030.     for(t = start_sub_group; t < s; t++){
  3031. #if    defined(DOS) && !defined(_WINDOWS)
  3032.         fseek(second_sort_file, sort[t] * sizeof(long), 0);
  3033.         fwrite((void *)&min_sub_group_date, sizeof(min_sub_group_date),
  3034.                        (size_t)1,  second_sort_file);
  3035. #else
  3036.     second_sort[sort[t]] = min_sub_group_date;
  3037. #endif
  3038.     }
  3039.  
  3040. #ifdef DEBUG
  3041.     for(t = 1; t <= max_msgno; t++) 
  3042.       dprint(9, (debugfile, "Second_sort[%3ld] is %3ld\n", t, second_sort[t]));
  3043. #endif
  3044.  
  3045.     /* Actually perform the sort */
  3046.     qsort(sort+1, (size_t)max_msgno, sizeof(long), compare_subject_2);
  3047.  
  3048.     /* Clean up */
  3049. #if    defined(DOS) && !defined(_WINDOWS)
  3050.     fclose(second_sort_file);
  3051.     unlink(second_sort_file_name);
  3052.     fs_give((void **)&second_sort_file_name);
  3053. #else    
  3054.     fs_give((void **)&second_sort);
  3055. #endif
  3056. }
  3057.  
  3058.  
  3059. /*
  3060.  * This is just used to help with the OrderedSubjSort.
  3061.  * The argument is a raw message number and the return value is the number
  3062.  * of seconds between the start of the BASEYEAR (1969) and the date in the
  3063.  * Date header.  Returns 0 if date can't be parsed or no date.
  3064.  */
  3065. long
  3066. seconds_since_epoch(msgno)
  3067. long msgno;
  3068. {
  3069.     ENVELOPE    *e;
  3070.     MESSAGECACHE mc;
  3071.     long seconds = 0L;
  3072.     int y, m;
  3073.  
  3074. #define MINUTE       (60L)
  3075. #define HOUR         (60L * MINUTE)
  3076. #define DAY          (24L * HOUR)
  3077. #define MONTH(mo,yr) ((long)days_in_month(mo,yr) * DAY)
  3078. #define YEAR(yr)     (((!((yr)%4L) && ((yr)%100L)) ? 366L : 365L) * DAY)
  3079.  
  3080.     e = mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
  3081.     if(e == NULL || e->date == NULL)
  3082.       return(0L);
  3083.  
  3084.     if(!mail_parse_date(&mc, e->date))
  3085.       return(0L);
  3086.  
  3087.     convert_to_gmt(&mc);
  3088.  
  3089.     for(y = 0; (unsigned)y < mc.year; y++)
  3090.       seconds += YEAR(y+BASEYEAR);
  3091.  
  3092.     for(m = 1; (unsigned)m < mc.month; m++)
  3093.       seconds += MONTH(m, mc.year + BASEYEAR);
  3094.     
  3095.     seconds +=
  3096.     ((mc.day-1) * DAY + mc.hours * HOUR + mc.minutes * MINUTE + mc.seconds);
  3097.  
  3098.     return(seconds);
  3099. }
  3100.  
  3101.  
  3102. /*----------------------------------------------------------------------
  3103.     Sort the current folder into the order set in the msgmap
  3104.  
  3105. Args: defer_on_intr   -- If we get interrupted before finishing, set the
  3106.               deferred variable.
  3107.       default_so      -- If we get interrupted before finishing, set the
  3108.               sort order to default_so.
  3109.       default_reverse -- If we get interrupted before finishing, set the
  3110.               revsort to default_reverse.
  3111.     
  3112.     The idea of the deferred sort is to let the user interrupt a long sort
  3113.     and have a chance to do a different command, such as a sort by arrival
  3114.     or a Goto.  The next newmail call will increment the deferred variable,
  3115.     then the user may do a command, then the newmail call after that
  3116.     causes the sort to happen if it is still needed.
  3117.   ----*/
  3118. void
  3119. sort_current_folder(defer_on_intr, default_so, default_reverse)
  3120.     int defer_on_intr;
  3121.     SortOrder default_so;
  3122.     int default_reverse;
  3123. {
  3124.     long        i, *sort, *saved_sort, total = mn_get_total(ps_global->msgmap);
  3125.     MSGNO_S    *sortmap = NULL;
  3126.     SortOrder   so;
  3127.     char        sort_msg[101];
  3128.     int         skip_qsort = 0, we_cancel = 0, rev, is_default_order;
  3129.  
  3130.     so = mn_get_sort(ps_global->msgmap);    /* current sort order */
  3131.     rev = mn_get_revsort(ps_global->msgmap);    /* currently reverse? */
  3132.     /*
  3133.      * If is_default_order, that means we aren't changing the sort order,
  3134.      * we're just sorting because we may have gotten new mail or unexcluded
  3135.      * some mail that was hidden.
  3136.      */
  3137.     is_default_order = (so == default_so && rev == default_reverse);
  3138.     if(!ps_global->msgmap || !(sort = ps_global->msgmap->sort))
  3139.       return;
  3140.  
  3141.     dprint(2, (debugfile, "Sorting by %s%s\n", sort_name(so),
  3142.            mn_get_revsort(ps_global->msgmap) ? "/reverse" : ""));
  3143.  
  3144.     /*
  3145.      * translate the selected numbers into an array of raw numbers
  3146.      * temporarily, then translate it back after the sort so the
  3147.      * same physical messages are selected...
  3148.      */
  3149.     mn_init(&sortmap, 0L);
  3150.     mn_set_cur(sortmap, mn_m2raw(ps_global->msgmap,
  3151.                  mn_first_cur(ps_global->msgmap)));
  3152.     while((i = mn_next_cur(ps_global->msgmap)) > 0L)
  3153.       mn_add_cur(sortmap, mn_m2raw(ps_global->msgmap, i));
  3154.  
  3155.     if(so == SortArrival){
  3156.     /*
  3157.      * BEWARE: "exclusion" may leave holes in the unsorted sort order
  3158.      * so we have to do a real sort if that is the case.
  3159.      */
  3160.     if(any_lflagged(ps_global->msgmap, MN_EXLD))
  3161.       qsort(sort+1, (size_t) total, sizeof(long), compare_arrival);
  3162.     else
  3163.       for(i = 1L; i <= total; i++)
  3164.         sort[i] = mn_get_revsort(ps_global->msgmap) ? (total + 1 - i) : i;
  3165.     }
  3166.     else{
  3167.         if(so == SortSubject || so == SortSubject2)    /* nmsgs cuz MN_EXLD */
  3168.       init_subject_cache(ps_global->mail_stream->nmsgs);
  3169.  
  3170.         /*========= Sorting ================================================*/
  3171. #ifndef DOS
  3172.         /*--- fetched map for keep track of progress of sort ----*/
  3173.         fetched_count = 0;
  3174.         fetched_map   = (unsigned short *)fs_get(
  3175.                          (ps_global->mail_stream->nmsgs/16 + 1)
  3176.                                               * sizeof(unsigned short));
  3177.     memset((void *)fetched_map, 0,
  3178.         (ps_global->mail_stream->nmsgs/16 + 1) * sizeof(unsigned short));
  3179. #endif
  3180.  
  3181.  
  3182. #ifdef DOS
  3183.     sprintf(sort_msg, "Sorting \"%.90s\"", ps_global->cur_folder);
  3184.     we_cancel = busy_alarm(1, sort_msg, NULL, 1);
  3185. #else
  3186.     sprintf(sort_msg, "Sorting \"%.90s\"", ps_global->cur_folder);
  3187.     we_cancel = busy_alarm(1, sort_msg, percent_sorted, 1);
  3188.  
  3189.     /*
  3190.      * Interruptible sorting is only available under unix where we
  3191.      * can rely on the tty driver to deliver the SIGINT.  If we
  3192.      * can safely work out a similar scheme under windows/max, this
  3193.      * #ifdef will have to get rearranged...
  3194.      */
  3195.     /* save array in case of interrupt */
  3196.     saved_sort
  3197.         = (long *)fs_get(ps_global->msgmap->sort_size * sizeof(long));
  3198.     for(i = 1L; i <= total; i++)
  3199.       saved_sort[i] = sort[i];
  3200.  
  3201.     if(setjmp(jump_past_qsort)){
  3202.         intr_handling_off();
  3203.         if(we_cancel)
  3204.           cancel_busy_alarm(-1);
  3205.  
  3206.         we_cancel = 0;
  3207.         skip_qsort = 1;
  3208.         /* restore default SortOrder */
  3209.         mn_set_sort(ps_global->msgmap, default_so);
  3210.         mn_set_revsort(ps_global->msgmap, default_reverse);
  3211.         /* restore sort array */
  3212.         for(i = 1L; i <= total; i++)
  3213.           sort[i] = saved_sort[i];
  3214.  
  3215.         fs_give((void **)&saved_sort); 
  3216.     }
  3217. #endif
  3218.  
  3219.     if(!skip_qsort){
  3220. #ifndef    DOS
  3221.         intr_handling_on();
  3222. #endif
  3223.  
  3224.         qsort(sort + 1, (size_t) total, sizeof(long),
  3225.           so == SortSubject  ? compare_subjects :
  3226.            so == SortFrom     ? compare_from :
  3227.             so == SortTo       ? compare_to :
  3228.              so == SortCc       ? compare_cc :
  3229.               so == SortDate     ? compare_message_dates :
  3230.                so == SortSubject2 ? compare_subjects:
  3231.                                          compare_size);
  3232.  
  3233.         /*---- special case -- reorder the groups of subjects ------*/
  3234.         if(so == SortSubject2) 
  3235.           second_subject_sort(sort, total, ps_global->mail_stream->nmsgs);
  3236. #ifndef    DOS
  3237.         intr_handling_off();
  3238.         fs_give((void **)&saved_sort); 
  3239. #endif
  3240.     }
  3241.  
  3242.     if(we_cancel)
  3243.       cancel_busy_alarm(1);
  3244.  
  3245.     if(skip_qsort)
  3246.       q_status_message3(SM_ORDER, 3, 3, "Sort cancelled%s%s%s",
  3247.           is_default_order ? "" : "; now sorting by ",
  3248.           is_default_order ? "" : sort_name(default_so),
  3249.           is_default_order ? "" : default_reverse ? "/reverse" : "");
  3250.  
  3251. #ifndef DOS
  3252.         fs_give((void **)&fetched_map); 
  3253. #endif
  3254.     clear_subject_cache();
  3255.     }
  3256.  
  3257.     if(!skip_qsort)
  3258.       ps_global->sort_is_deferred = 0;  /* finished sort, cancel deferral */
  3259.     else if(defer_on_intr)
  3260.       ps_global->sort_is_deferred = 1;
  3261.  
  3262.     /*
  3263.      * restore the selected array of message numbers.  No expunge could
  3264.      * have happened yet, so we don't worry about raw2m returning 0...
  3265.      */
  3266.     mn_reset_cur(ps_global->msgmap, mn_raw2m(ps_global->msgmap,
  3267.                          mn_first_cur(sortmap)));
  3268.     while((i = mn_next_cur(sortmap)) > 0L)
  3269.       mn_add_cur(ps_global->msgmap, mn_raw2m(ps_global->msgmap, i));
  3270.  
  3271.     mn_give(&sortmap);
  3272. }
  3273.  
  3274.  
  3275. /*----------------------------------------------------------------------
  3276.     Map sort types to names
  3277.   ----*/
  3278. char *    
  3279. sort_name(so)
  3280.   SortOrder so;
  3281. {
  3282.     /*
  3283.      * Make sure the first upper case letter of any new sort name is
  3284.      * unique.  The command char and label for sort selection is 
  3285.      * derived from this name and its first upper case character.
  3286.      * See mailcmd.c:select_sort().
  3287.      */
  3288.     return((so == SortArrival)  ? "Arrival" :
  3289.         (so == SortDate)     ? "Date" :
  3290.          (so == SortSubject)  ? "Subject" :
  3291.           (so == SortCc)       ? "Cc" :
  3292.            (so == SortFrom)        ? "From" :
  3293.         (so == SortTo)         ? "To" :
  3294.          (so == SortSize)     ? "siZe" :
  3295.           (so == SortSubject2) ? "OrderedSubj" :
  3296.                       "BOTCH");
  3297. }
  3298.  
  3299.  
  3300.  
  3301. /*----------------------------------------------------------------------
  3302.     initialize the subject cache
  3303.   ----*/
  3304. void
  3305. init_subject_cache(n)
  3306.     long n;
  3307. {
  3308.     scache = (struct scache *) fs_get(sizeof(struct scache));
  3309.     memset((void *)scache, 0, sizeof(struct scache));
  3310.     scache->size = n;
  3311. #if    defined(DOS) && !defined(_WINDOWS)
  3312.     scache->cname = temp_nam(NULL, "sc");
  3313.     scache->cfile = fopen(scache->cname, "wb+");
  3314.     /* BUG? go into non-caching mode if failure below? */
  3315.     while(n-- >= 0L){
  3316.     if(fwrite(&scache->ent[1], sizeof(scache_ent), (size_t)1,
  3317.           scache->cfile) != 1){
  3318.         sprintf(tmp_20k_buf, "subject cache: %s",
  3319.             error_description(errno));
  3320.         fatal(tmp_20k_buf);
  3321.     }
  3322.     }
  3323. #else
  3324.     scache->ent  = (char **)fs_get((size_t)(n+1L) * sizeof(char *));
  3325.     memset((void *)scache->ent, 0, (size_t)(n+1L) * sizeof(char *));
  3326. #endif
  3327. }
  3328.  
  3329.  
  3330. /*----------------------------------------------------------------------
  3331.     return the subject of the given number if it's in the cache, else NULL
  3332.   ----*/
  3333. char *
  3334. subject_cache_ent(n)
  3335.     long n;
  3336. {
  3337. #if    defined(DOS) && !defined(_WINDOWS)
  3338.     char *buf;
  3339.     int   i;
  3340.  
  3341.     /* if the one we want's in core, return it */
  3342.     buf = (scache->msgno[i=0] == n || scache->msgno[i=1] == n)
  3343.         ? scache->ent[i].buf : NULL;
  3344.  
  3345.     /* else pick one of the slots, and fetch n's subject off disk */
  3346.     if(!buf){
  3347.     i = subject_cache_slot(n);
  3348.     buf = scache->ent[i].used ? scache->ent[i].buf : NULL;
  3349.     }
  3350.  
  3351.     scache->last = i;
  3352.     return(buf);
  3353. #else
  3354.     return(scache->ent[n]);
  3355. #endif
  3356. }
  3357.  
  3358.  
  3359. /*----------------------------------------------------------------------
  3360.     add the given subject to the cache
  3361.   ----*/
  3362. char *
  3363. subject_cache_add(n, s)
  3364.     long n;
  3365.     char *s;
  3366. {
  3367. #if    defined(DOS) && !defined(_WINDOWS)
  3368.     int i;
  3369.     
  3370.     if(scache->msgno[i=0] != n && scache->msgno[i=1] != n)
  3371.       i = subject_cache_slot(n);
  3372.  
  3373.     scache->last = i;
  3374.     scache->ent[i].used = 1;
  3375.     scache->msgno[i] = n;
  3376.     strncpy(scache->ent[i].buf, s, SUB_CACHE_LEN-1);
  3377.     scache->ent[i].buf[SUB_CACHE_LEN-1] = '\0';
  3378.     return(scache->ent[i].buf);
  3379. #else
  3380.     return(scache->ent[n] = cpystr(s));
  3381. #endif
  3382. }
  3383.  
  3384.  
  3385. /*----------------------------------------------------------------------
  3386.     pick the next slot, and fetch n's subject off disk
  3387.   ----*/
  3388. int
  3389. subject_cache_slot(n)
  3390.     long n;
  3391. {
  3392. #if    defined(DOS) && !defined(_WINDOWS)
  3393.     int i;
  3394.  
  3395.     i = scache->last ? 0 : 1;
  3396.     if(scache->msgno[i]){
  3397.     fseek(scache->cfile, scache->msgno[i] * sizeof(scache_ent), 0);
  3398.     fwrite(&scache->ent[i], sizeof(scache_ent), (size_t)1,
  3399.            scache->cfile);
  3400.     }
  3401.  
  3402.     scache->msgno[i] = n;
  3403.     if(fseek(scache->cfile, scache->msgno[i] * sizeof(scache_ent), 0)
  3404.        || fread(&scache->ent[i], sizeof(scache_ent), (size_t)1,
  3405.         scache->cfile) != 1){
  3406.     sprintf(tmp_20k_buf, "Can't access subject cache: ",
  3407.         error_description(errno));
  3408.     fatal(tmp_20k_buf);
  3409.     }
  3410.  
  3411.     return(i);
  3412. #endif
  3413. }
  3414.  
  3415.  
  3416. /*----------------------------------------------------------------------
  3417.     flush the subject cache
  3418.   ----*/
  3419. void
  3420. clear_subject_cache()
  3421. {
  3422.     if(scache){
  3423. #if    defined(DOS) && !defined(_WINDOWS)
  3424.     fclose(scache->cfile);
  3425.     unlink(scache->cname);
  3426.     fs_give((void **)&scache->cname);
  3427. #else
  3428.     for(; scache->size; scache->size--)
  3429.       if(scache->ent[scache->size])
  3430.         fs_give((void **)&scache->ent[scache->size]);
  3431.  
  3432.     if(scache->ent)
  3433.       fs_give((void **)&scache->ent);
  3434.  
  3435. #endif
  3436.     fs_give((void **)&scache);
  3437.     }
  3438. }
  3439.  
  3440.  
  3441.  
  3442. /*
  3443.  *           * * *  Message number management functions  * * *
  3444.  */
  3445.  
  3446.  
  3447. /*----------------------------------------------------------------------
  3448.   Initialize a message manipulation structure for the given total
  3449.  
  3450.    Accepts: msgs - pointer to message manipulation struct
  3451.         n - number to test
  3452.    Returns: true if n is in selected array, false otherwise
  3453.  
  3454.   ----*/
  3455. void
  3456. msgno_init(msgs, tot)
  3457.      MSGNO_S **msgs;
  3458.      long      tot;
  3459. {
  3460.     long   slop = (tot + 1L) % 64;
  3461.     size_t len;
  3462.  
  3463.     if(!msgs)
  3464.       return;
  3465.  
  3466.     if(!(*msgs)){
  3467.     (*msgs) = (MSGNO_S *)fs_get(sizeof(MSGNO_S));
  3468.     memset((void *)(*msgs), 0, sizeof(MSGNO_S));
  3469.     }
  3470.  
  3471.     (*msgs)->sel_cur  = 0L;
  3472.     (*msgs)->sel_cnt  = 1L;
  3473.     (*msgs)->sel_size = 8L;
  3474.     len              = (size_t)(*msgs)->sel_size * sizeof(long);
  3475.     if((*msgs)->select)
  3476.       fs_resize((void **)&((*msgs)->select), len);
  3477.     else
  3478.       (*msgs)->select = (long *)fs_get(len);
  3479.  
  3480.     (*msgs)->select[0] = (tot) ? 1L : 0L;
  3481.  
  3482.     (*msgs)->sort_size = (tot + 1L) + (64 - slop);
  3483.     len               = (size_t)(*msgs)->sort_size * sizeof(long);
  3484.     if((*msgs)->sort)
  3485.       fs_resize((void **)&((*msgs)->sort), len);
  3486.     else
  3487.       (*msgs)->sort = (long *)fs_get(len);
  3488.  
  3489.     memset((void *)(*msgs)->sort, 0, len);
  3490.     for(slop = 1L ; slop <= tot; slop++)    /* reusing "slop" */
  3491.       (*msgs)->sort[slop] = slop;
  3492.  
  3493.     (*msgs)->max_msgno    = tot;
  3494.     (*msgs)->sort_order   = ps_global->def_sort;
  3495.     (*msgs)->reverse_sort = ps_global->def_sort_rev;
  3496.     (*msgs)->flagged_hid  = 0L;
  3497.     (*msgs)->flagged_exld = 0L;
  3498.     (*msgs)->flagged_tmp  = 0L;
  3499. }
  3500.  
  3501.  
  3502.  
  3503. /*----------------------------------------------------------------------
  3504.  Increment the current message number
  3505.  
  3506.    Accepts: msgs - pointer to message manipulation struct
  3507.   ----*/
  3508. void
  3509. msgno_inc(stream, msgs)
  3510.      MAILSTREAM *stream;
  3511.      MSGNO_S    *msgs;
  3512. {
  3513.     long i;
  3514.  
  3515.     if(!msgs || mn_get_total(msgs) < 1L)
  3516.       return;
  3517.  
  3518.     for(i = msgs->select[msgs->sel_cur] + 1; i <= mn_get_total(msgs); i++){
  3519.     if(!get_lflag(stream, msgs, i, MN_HIDE)){
  3520.         (msgs)->select[((msgs)->sel_cur)] = i;
  3521.         break;
  3522.     }
  3523.     }
  3524. }
  3525.  
  3526.  
  3527.  
  3528. /*----------------------------------------------------------------------
  3529.   Decrement the current message number
  3530.  
  3531.    Accepts: msgs - pointer to message manipulation struct
  3532.   ----*/
  3533. void
  3534. msgno_dec(stream, msgs)
  3535.      MAILSTREAM *stream;
  3536.      MSGNO_S     *msgs;
  3537. {
  3538.     long i;
  3539.  
  3540.     if(!msgs || mn_get_total(msgs) < 1L)
  3541.       return;
  3542.  
  3543.     for(i = (msgs)->select[((msgs)->sel_cur)] - 1L; i >= 1L; i--){
  3544.     if(!get_lflag(stream, msgs, i, MN_HIDE)){
  3545.         (msgs)->select[((msgs)->sel_cur)] = i;
  3546.         break;
  3547.     }
  3548.     }
  3549. }
  3550.  
  3551.  
  3552.  
  3553. /*----------------------------------------------------------------------
  3554.   Got thru the message mapping table, and remove messages with DELETED flag
  3555.  
  3556.    Accepts: stream -- mail stream to removed message references from
  3557.         msgs -- pointer to message manipulation struct
  3558.         f -- flags to use a purge criteria
  3559.   ----*/
  3560. void
  3561. msgno_exclude(stream, msgs)
  3562.      MAILSTREAM *stream;
  3563.      MSGNO_S     *msgs;
  3564. {
  3565.     long      i, j;
  3566.  
  3567.     if(!msgs || msgs->max_msgno < 1L)
  3568.       return;
  3569.  
  3570.     /*
  3571.      * With 3.91 we're using a new strategy for finding and operating
  3572.      * on all the messages with deleted status.  The idea is to do a
  3573.      * mail_search for deleted messages so the elt's "searched" bit gets
  3574.      * set, and then to scan the elt's for them and set our local bit
  3575.      * to indicate they're excluded...
  3576.      */
  3577.     (void)count_flagged(stream, "DELETED");
  3578.  
  3579.     for(i = 1L; i <= msgs->max_msgno; ){
  3580.     if(mail_elt(stream, mn_m2raw(msgs, i))->searched){
  3581.         /*--- clear all flags to keep our counts consistent  ---*/
  3582.         set_lflag(stream, msgs, i, (MN_HIDE|MN_SLCT), 0);
  3583.         set_lflag(stream, msgs, i, MN_EXLD, 1); /* mark excluded */
  3584.  
  3585.         /* --- erase knowledge in sort array (shift array down) --- */
  3586.         for(j = i + 1; j <= msgs->max_msgno; j++)
  3587.           msgs->sort[j-1] = msgs->sort[j];
  3588. /* BUG: should compress strings of deleted message numbers */
  3589.  
  3590.         msgs->max_msgno = max(0L, msgs->max_msgno - 1L);
  3591.         msgno_flush_selected(msgs, i);
  3592.     }
  3593.     else
  3594.       i++;
  3595.     }
  3596.  
  3597.     /*
  3598.      * If we excluded away a zoomed display, unhide everything...
  3599.      */
  3600.     if(msgs->max_msgno > 0L && any_lflagged(msgs, MN_HIDE) >= msgs->max_msgno)
  3601.       for(i = 1L; i <= msgs->max_msgno; i++)
  3602.     set_lflag(stream, msgs, i, MN_HIDE, 0);
  3603. }
  3604.  
  3605.  
  3606.  
  3607. /*----------------------------------------------------------------------
  3608.   Got thru the message mapping table, and remove messages with given flag
  3609.  
  3610.    Accepts: stream -- mail stream to removed message references from
  3611.         msgs -- pointer to message manipulation struct
  3612.         f -- flags to use a purge criteria
  3613.   ----*/
  3614. void
  3615. msgno_include(stream, msgs)
  3616.      MAILSTREAM *stream;
  3617.      MSGNO_S     *msgs;
  3618. {
  3619.     long   i, slop, old_total, old_size;
  3620.     size_t len;
  3621.  
  3622.     for(i = 1L; i <= stream->nmsgs; i++)
  3623.       if(get_lflag(stream, NULL, i, MN_EXLD)){
  3624.       old_total        = msgs->max_msgno;
  3625.       old_size         = msgs->sort_size;
  3626.       slop             = (msgs->max_msgno + 1L) % 64;
  3627.       msgs->sort_size  = (msgs->max_msgno + 1L) + (64 - slop);
  3628.       len           = (size_t) msgs->sort_size * sizeof(long);
  3629.       if(msgs->sort){
  3630.           if(old_size != msgs->sort_size)
  3631.         fs_resize((void **)&(msgs->sort), len);
  3632.       }
  3633.       else
  3634.         msgs->sort = (long *)fs_get(len);
  3635.  
  3636.       msgs->sort[++msgs->max_msgno] = i;
  3637.       set_lflag(stream, msgs, msgs->max_msgno, MN_EXLD, 0);
  3638.  
  3639.       if(old_total <= 0L){            /* if no previous messages, */
  3640.           if(!msgs->select){        /* select the new message   */
  3641.           msgs->sel_size = 8L;
  3642.           len         = (size_t)msgs->sel_size * sizeof(long);
  3643.           msgs->select   = (long *)fs_get(len);
  3644.           }
  3645.  
  3646.           msgs->sel_cnt   = 1L;
  3647.           msgs->sel_cur   = 0L;
  3648.           msgs->select[0] = 1L;
  3649.       }
  3650.       }
  3651. }
  3652.  
  3653.  
  3654.  
  3655. /*----------------------------------------------------------------------
  3656.  Add the given number of raw message numbers to the end of the
  3657.  current list...
  3658.  
  3659.    Accepts: msgs - pointer to message manipulation struct
  3660.         n - number to add
  3661.    Returns: with fixed up msgno struct
  3662.  
  3663.    Only have to adjust the sort array, as since new mail can't cause
  3664.    selection!
  3665.   ----*/
  3666. void
  3667. msgno_add_raw(msgs, n)
  3668.      MSGNO_S *msgs;
  3669.      long     n;
  3670. {
  3671.     long   slop, old_total, old_size;
  3672.     size_t len;
  3673.  
  3674.     if(!msgs || n <= 0L)
  3675.       return;
  3676.  
  3677.     old_total        = msgs->max_msgno;
  3678.     old_size         = msgs->sort_size;
  3679.     msgs->max_msgno += n;
  3680.     slop             = (msgs->max_msgno + 1L) % 64;
  3681.     msgs->sort_size  = (msgs->max_msgno + 1L) + (64 - slop);
  3682.     len             = (size_t) msgs->sort_size * sizeof(long);
  3683.     if(msgs->sort){
  3684.     if(old_size != msgs->sort_size)
  3685.       fs_resize((void **)&(msgs->sort), len);
  3686.     }
  3687.     else
  3688.       msgs->sort = (long *)fs_get(len);
  3689.  
  3690.     for(slop = old_total + 1L; slop <= msgs->max_msgno; slop++)
  3691.       msgs->sort[slop] = slop;
  3692.  
  3693.     if(old_total <= 0L){            /* if no previous messages, */
  3694.     if(!msgs->select){            /* select the new message   */
  3695.         msgs->sel_size = 8L;
  3696.         len           = (size_t)msgs->sel_size * sizeof(long);
  3697.         msgs->select   = (long *)fs_get(len);
  3698.     }
  3699.  
  3700.     msgs->sel_cnt   = 1L;
  3701.     msgs->sel_cur   = 0L;
  3702.     msgs->select[0] = 1L;
  3703.     }
  3704. }
  3705.  
  3706.  
  3707.  
  3708. /*----------------------------------------------------------------------
  3709.   Remove all knowledge of the given raw message number
  3710.  
  3711.    Accepts: msgs - pointer to message manipulation struct
  3712.         n - number to remove
  3713.    Returns: with fixed up msgno struct
  3714.  
  3715.    After removing *all* references, adjust the sort array and
  3716.    various pointers accordingly...
  3717.   ----*/
  3718. void
  3719. msgno_flush_raw(msgs, n)
  3720.      MSGNO_S *msgs;
  3721.      long     n;
  3722. {
  3723.     long i, old_sorted = 0L;
  3724.     int  shift = 0;
  3725.  
  3726.     if(!msgs)
  3727.       return;
  3728.  
  3729.     /*---- blast n from sort array ----*/
  3730.     for(i = 1L; i <= msgs->max_msgno; i++){
  3731.     if(msgs->sort[i] == n){
  3732.         old_sorted = i;
  3733.         shift++;
  3734.     }
  3735.  
  3736.     if(shift)
  3737.       msgs->sort[i] = msgs->sort[i + 1L];
  3738.  
  3739.     if(msgs->sort[i] > n)
  3740.       msgs->sort[i] -= 1L;
  3741.     }
  3742.  
  3743.     /*---- now, fixup select array ----*/
  3744.     msgs->max_msgno = max(0L, msgs->max_msgno - 1L);
  3745.     msgno_flush_selected(msgs, old_sorted);
  3746. }
  3747.  
  3748.  
  3749.  
  3750. /*----------------------------------------------------------------------
  3751.   Remove all knowledge of the given selected message number
  3752.  
  3753.    Accepts: msgs - pointer to message manipulation struct
  3754.         n - number to remove
  3755.    Returns: with fixed up selec members in msgno struct
  3756.  
  3757.    Remove reference and fix up selected message numbers beyond
  3758.    the specified number
  3759.   ----*/
  3760. void
  3761. msgno_flush_selected(msgs, n)
  3762.      MSGNO_S *msgs;
  3763.      long     n;
  3764. {
  3765.     long i;
  3766.     int  shift = 0;
  3767.  
  3768.     for(i = 0L; i < msgs->sel_cnt; i++){
  3769.     if(!shift && (msgs->select[i] == n))
  3770.       shift++;
  3771.  
  3772.     if(shift && i + 1L < msgs->sel_cnt)
  3773.       msgs->select[i] = msgs->select[i + 1L];
  3774.  
  3775.     if(n < msgs->select[i] || msgs->select[i] > msgs->max_msgno)
  3776.       msgs->select[i] -= 1L;
  3777.     }
  3778.  
  3779.     if(shift && msgs->sel_cnt > 1L)
  3780.       msgs->sel_cnt -= 1L;
  3781. }
  3782.  
  3783.  
  3784.  
  3785. /*----------------------------------------------------------------------
  3786.   Test to see if the given message number is in the selected message
  3787.   list...
  3788.  
  3789.    Accepts: msgs - pointer to message manipulation struct
  3790.         n - number to test
  3791.    Returns: true if n is in selected array, false otherwise
  3792.  
  3793.   ----*/
  3794. int
  3795. msgno_in_select(msgs, n)
  3796.      MSGNO_S *msgs;
  3797.      long     n;
  3798. {
  3799.     long i;
  3800.  
  3801.     if(msgs)
  3802.       for(i = 0L; i < msgs->sel_cnt; i++)
  3803.     if(msgs->select[i] == n)
  3804.       return(1);
  3805.  
  3806.     return(0);
  3807. }
  3808.  
  3809.  
  3810.  
  3811. /*----------------------------------------------------------------------
  3812.   return our index number for the given raw message number
  3813.  
  3814.    Accepts: msgs - pointer to message manipulation struct
  3815.         n - number to locate
  3816.    Returns: our index number of given raw message
  3817.  
  3818.   ----*/
  3819. long
  3820. msgno_in_sort(msgs, n)
  3821.      MSGNO_S *msgs;
  3822.      long     n;
  3823. {
  3824.     static long start = 1L;
  3825.     long        i;
  3826.  
  3827.     if(mn_get_total(msgs) < 1L)
  3828.       return(-1L);
  3829.     else if(mn_get_sort(msgs) == SortArrival && !any_lflagged(msgs, MN_EXLD))
  3830.       return((mn_get_revsort(msgs)) ? 1 + mn_get_total(msgs) - n  : n);
  3831.  
  3832.     if(start > mn_get_total(msgs))        /* reset start? */
  3833.       start = 1L;
  3834.  
  3835.     i = start;
  3836.     do {
  3837.     if(mn_m2raw(msgs, i) == n)
  3838.       return(start = i);
  3839.  
  3840.     if(++i > mn_get_total(msgs))
  3841.       i = 1L;
  3842.     }
  3843.     while(i != start);
  3844.  
  3845.     return(0L);
  3846. }
  3847.  
  3848.  
  3849.  
  3850. /*
  3851.  *           * * *  Index entry cache manager  * * *
  3852.  */
  3853.  
  3854. /*
  3855.  * at some point, this could be made part of the pine_state struct.
  3856.  * the only changes here would be to pass the ps pointer around
  3857.  */
  3858. static struct index_cache {
  3859.    void      *cache;                /* pointer to cache         */
  3860.    char      *name;                /* pointer to cache name    */
  3861.    long    num;                    /* # of last index in cache */
  3862.    size_t  size;                /* size of each index line  */
  3863.    int     need_format_setup;
  3864. } icache = { (void *) NULL, (char *) NULL, (long) 0, (size_t) 0, (int) 0 };
  3865.   
  3866. /*
  3867.  * cache size growth increment
  3868.  */
  3869.  
  3870. #ifdef    DOS
  3871. /*
  3872.  * the idea is to have the cache increment be a multiple of the block
  3873.  * size (4K), for efficient swapping of blocks.  we can pretty much
  3874.  * assume 81 character lines.
  3875.  *
  3876.  * REMEMBER: number of lines in the incore cache has to be a multiple 
  3877.  *           of the cache growth increment!
  3878.  */
  3879. #define    IC_SIZE        (50L)            /* cache growth increment  */
  3880. #define    ICC_SIZE    (50L)            /* enties in incore cache  */
  3881. #define FUDGE           (46L)            /* extra chars to make 4096*/
  3882.  
  3883. static char    *incore_cache = NULL;        /* pointer to incore cache */
  3884. static long      cache_block_s = 0L;        /* save recomputing time   */
  3885. static long      cache_base = 0L;        /* index of line 0 in block*/
  3886. #else
  3887. #define    IC_SIZE        100
  3888. #endif
  3889.  
  3890. /*
  3891.  * important values for cache building
  3892.  */
  3893. static MAILSTREAM *bc_this_stream = NULL;
  3894. static long  bc_start, bc_current;
  3895. static short bc_done = 0;
  3896.  
  3897.  
  3898. /*
  3899.  * way to return the current cache entry size
  3900.  */
  3901. int
  3902. i_cache_width()
  3903. {
  3904.     return(icache.size - sizeof(HLINE_S));
  3905. }
  3906.  
  3907.  
  3908. /* 
  3909.  * i_cache_size - make sure the cache is big enough to contain
  3910.  * requested entry
  3911.  */
  3912. int
  3913. i_cache_size(indx)
  3914.     long         indx;
  3915. {
  3916.     long j;
  3917.     size_t  newsize = sizeof(HLINE_S)
  3918.              + (max(ps_global->ttyo->screen_cols, 80) * sizeof(char));
  3919.  
  3920.     if(j = (newsize % sizeof(long)))        /* alignment hack */
  3921.       newsize += (sizeof(long) - (size_t)j);
  3922.  
  3923.     if(icache.size != newsize){
  3924.     clear_index_cache();            /* clear cache, start over! */
  3925.     icache.size = newsize;
  3926.     }
  3927.  
  3928.     if(indx > (j = icache.num - 1L)){        /* make room for entry! */
  3929.     size_t  tmplen = icache.size;
  3930.     char   *tmpline;
  3931.  
  3932.     while(indx >= icache.num)
  3933.       icache.num += IC_SIZE;
  3934.  
  3935. #ifdef    DOS
  3936.     tmpline = fs_get(tmplen);
  3937.     memset(tmpline, 0, tmplen);
  3938.     if(icache.cache == NULL){
  3939.         if(!icache.name)
  3940.           icache.name = temp_nam(NULL, "pi");
  3941.  
  3942.         if((icache.cache = (void *)fopen(icache.name,"w+b")) == NULL){
  3943.         sprintf(tmp_20k_buf, "Can't open index cache: %s",icache.name);
  3944.         fatal(tmp_20k_buf);
  3945.         }
  3946.  
  3947.         for(j = 0; j < icache.num; j++){
  3948.             if(fwrite(tmpline,tmplen,(size_t)1,(FILE *)icache.cache) != 1)
  3949.           fatal("Can't write index cache in resize");
  3950.  
  3951.         if(j%ICC_SIZE == 0){
  3952.           if(fwrite(tmpline,(size_t)FUDGE,
  3953.                 (size_t)1,(FILE *)icache.cache) != 1)
  3954.             fatal("Can't write FUDGE factor in resize");
  3955.             }
  3956.         }
  3957.     }
  3958.     else{
  3959.         /* init new entries */
  3960.         fseek((FILE *)icache.cache, 0L, 2);        /* seek to end */
  3961.  
  3962.         for(;j < icache.num; j++){
  3963.             if(fwrite(tmpline,tmplen,(size_t)1,(FILE *)icache.cache) != 1)
  3964.           fatal("Can't write index cache in resize");
  3965.  
  3966.         if(j%ICC_SIZE == 0){
  3967.           if(fwrite(tmpline,(size_t)FUDGE,
  3968.                 (size_t)1,(FILE *)icache.cache) != 1)
  3969.             fatal("Can't write FUDGE factor in resize");
  3970.             }
  3971.         }
  3972.     }
  3973.  
  3974.     fs_give((void **)&tmpline);
  3975. #else
  3976.     if(icache.cache == NULL){
  3977.         icache.cache = (void *)fs_get((icache.num+1)*tmplen);
  3978.         memset(icache.cache, 0, (icache.num+1)*tmplen);
  3979.     }
  3980.     else{
  3981.             fs_resize((void **)&(icache.cache), (size_t)(icache.num+1)*tmplen);
  3982.         tmpline = (char *)icache.cache + ((j+1) * tmplen);
  3983.         memset(tmpline, 0, (icache.num - j) * tmplen);
  3984.     }
  3985. #endif
  3986.     }
  3987.  
  3988.     return(1);
  3989. }
  3990.  
  3991. #ifdef    DOS
  3992. /*
  3993.  * read a block into the incore cache
  3994.  */
  3995. void
  3996. icread()
  3997. {
  3998.     size_t n;
  3999.  
  4000.     if(fseek((FILE *)icache.cache, (cache_base/ICC_SIZE) * cache_block_s, 0))
  4001.       fatal("ran off end of index cache file in icread");
  4002.  
  4003.     n = fread((void *)incore_cache, (size_t)cache_block_s, 
  4004.         (size_t)1, (FILE *)icache.cache);
  4005.  
  4006.     if(n != 1L)
  4007.       fatal("Can't read index cache block in from disk");
  4008. }
  4009.  
  4010.  
  4011. /*
  4012.  * write the incore cache out to disk
  4013.  */
  4014. void
  4015. icwrite()
  4016. {
  4017.     size_t n;
  4018.  
  4019.     if(fseek((FILE *)icache.cache, (cache_base/ICC_SIZE) * cache_block_s, 0))
  4020.       fatal("ran off end of index cache file in icwrite");
  4021.  
  4022.     n = fwrite((void *)incore_cache, (size_t)cache_block_s,
  4023.         (size_t)1, (FILE *)icache.cache);
  4024.  
  4025.     if(n != 1L)
  4026.       fatal("Can't write index cache block in from disk");
  4027. }
  4028.  
  4029.  
  4030. /*
  4031.  * make sure the neccessary block of index lines is in core
  4032.  */
  4033. void
  4034. i_cache_hit(indx)
  4035.     long         indx;
  4036. {
  4037.     dprint(9, (debugfile, "i_cache_hit: %ld\n", indx));
  4038.     /* no incore cache, create it */
  4039.     if(!incore_cache){
  4040.     cache_block_s = (((long)icache.size * ICC_SIZE) + FUDGE)*sizeof(char);
  4041.     incore_cache  = (char *)fs_get((size_t)cache_block_s);
  4042.     cache_base = (indx/ICC_SIZE) * ICC_SIZE;
  4043.     icread();
  4044.     return;
  4045.     }
  4046.  
  4047.     if(indx >= cache_base && indx < (cache_base + ICC_SIZE))
  4048.     return;
  4049.  
  4050.     icwrite();
  4051.  
  4052.     cache_base = (indx/ICC_SIZE) * ICC_SIZE;
  4053.     icread();
  4054. }
  4055. #endif
  4056.  
  4057.  
  4058. /*
  4059.  * return the index line associated with the given message number
  4060.  */
  4061. HLINE_S *
  4062. get_index_cache(msgno)
  4063.     long         msgno;
  4064. {
  4065.     if(!i_cache_size(--msgno)){
  4066.     q_status_message(SM_ORDER, 0, 3, "get_index_cache failed!");
  4067.     return(NULL);
  4068.     }
  4069.  
  4070. #ifdef    DOS
  4071.     i_cache_hit(msgno);            /* get entry into core */
  4072.     return((HLINE_S *)(incore_cache 
  4073.           + ((msgno%ICC_SIZE) * (long)max(icache.size,FUDGE))));
  4074. #else
  4075.     return((HLINE_S *) ((char *)(icache.cache) 
  4076.        + (msgno * (long)icache.size * sizeof(char))));
  4077. #endif
  4078. }
  4079.  
  4080.  
  4081. /*
  4082.  * the idea is to pre-build and cache index lines while waiting
  4083.  * for command input.
  4084.  */
  4085. void
  4086. build_header_cache()
  4087. {
  4088.     long lines_per_page = max(0,ps_global->ttyo->screen_rows - 5);
  4089.  
  4090.     if(mn_get_total(ps_global->msgmap) == 0 || ps_global->mail_stream == NULL
  4091.        || (bc_this_stream == ps_global->mail_stream && bc_done >= 2)
  4092.        || any_lflagged(ps_global->msgmap, (MN_HIDE|MN_EXLD|MN_SLCT)))
  4093.       return;
  4094.  
  4095.     if(bc_this_stream != ps_global->mail_stream){ /* reset? */
  4096.     bc_this_stream = ps_global->mail_stream;
  4097.     bc_current = bc_start = top_ent_calc(ps_global->mail_stream,
  4098.                          ps_global->msgmap,
  4099.                          mn_get_cur(ps_global->msgmap),
  4100.                          lines_per_page);
  4101.     bc_done  = 0;
  4102.     }
  4103.  
  4104.     if(!bc_done && bc_current > mn_get_total(ps_global->msgmap)){ /* wrap? */
  4105.     bc_current = bc_start - lines_per_page;
  4106.     bc_done++;
  4107.     }
  4108.     else if(bc_done == 1 && (bc_current % lines_per_page) == 1)
  4109.       bc_current -= (2L * lines_per_page);
  4110.  
  4111.     if(bc_current < 1)
  4112.       bc_done = 2;            /* really done! */
  4113.     else
  4114.       (void)build_header_line(ps_global, ps_global->mail_stream,
  4115.                   ps_global->msgmap, bc_current++);
  4116. }
  4117.  
  4118.  
  4119. /*
  4120.  * erase a particular entry in the cache
  4121.  */
  4122. void
  4123. clear_index_cache_ent(indx)
  4124.     long indx;
  4125. {
  4126.     HLINE_S *tmp = get_index_cache(indx);
  4127.  
  4128.     tmp->line[0] = '\0';
  4129.     tmp->id      = 0L;
  4130. }
  4131.  
  4132.  
  4133. /*
  4134.  * clear the index cache associated with the current mailbox
  4135.  */
  4136. void
  4137. clear_index_cache()
  4138. {
  4139. #ifdef    DOS
  4140.     cache_base = 0L;
  4141.     if(incore_cache)
  4142.       fs_give((void **)&incore_cache);
  4143.  
  4144.     if(icache.cache){
  4145.     fclose((FILE *)icache.cache);
  4146.     icache.cache = NULL;
  4147.     }
  4148.  
  4149.     if(icache.name){
  4150.     unlink(icache.name);
  4151.     fs_give((void **)&icache.name);
  4152.     }
  4153. #else
  4154.     if(icache.cache)
  4155.       fs_give((void **)&(icache.cache));
  4156. #endif
  4157.     icache.num  = 0L;
  4158.     icache.size = 0;
  4159.     bc_this_stream = NULL;
  4160.     set_need_format_setup();
  4161. }
  4162.  
  4163.  
  4164. void
  4165. set_need_format_setup()
  4166. {
  4167.     icache.need_format_setup = 1;
  4168. }
  4169.  
  4170.  
  4171. void
  4172. clear_need_format_setup()
  4173. {
  4174.     icache.need_format_setup = 0;
  4175. }
  4176.  
  4177.  
  4178. int
  4179. check_need_format_setup()
  4180. {
  4181.     return(icache.need_format_setup);
  4182. }
  4183.  
  4184.  
  4185. #ifdef    DOS
  4186. /*
  4187.  * flush the incore_cache, but not the whole enchilada
  4188.  */
  4189. void
  4190. flush_index_cache()
  4191. {
  4192.     if(incore_cache){
  4193.     if(mn_get_total(ps_global->msgmap) > 0L)
  4194.       icwrite();            /* write this block out to disk */
  4195.  
  4196.     fs_give((void **)&incore_cache);
  4197.     cache_base = 0L;
  4198.     }
  4199. }
  4200. #endif
  4201.  
  4202.  
  4203. #ifdef _WINDOWS
  4204. /*----------------------------------------------------------------------
  4205.   Callback to get the text of the current message.  Used to display
  4206.   a message in an alternate window.      
  4207.  
  4208.   Args: cmd - what type of scroll operation.
  4209.     text - filled with pointer to text.
  4210.     l - length of text.
  4211.     style - Returns style of text.  Can be:
  4212.         GETTEXT_TEXT - Is a pointer to text with CRLF deliminated
  4213.                 lines
  4214.         GETTEXT_LINES - Is a pointer to NULL terminated array of
  4215.                 char *.  Each entry points to a line of
  4216.                 text.
  4217.                     
  4218.         this implementation always returns GETTEXT_TEXT.
  4219.  
  4220.   Returns: TRUE - did the scroll operation.
  4221.        FALSE - was not able to do the scroll operation.
  4222.  ----*/
  4223. int
  4224. index_scroll_callback (cmd, scroll_pos)
  4225. int    cmd;
  4226. long    scroll_pos;
  4227. {
  4228.     int paint = TRUE;
  4229.     
  4230.     switch (cmd) {
  4231.       case MSWIN_KEY_SCROLLUPLINE:
  4232.     paint = index_scroll_up (1);
  4233.     break;
  4234.  
  4235.       case MSWIN_KEY_SCROLLDOWNLINE:
  4236.     paint = index_scroll_down (1);
  4237.     break;
  4238.  
  4239.       case MSWIN_KEY_SCROLLUPPAGE:
  4240.     paint = index_scroll_up (current_index_state->lines_per_page);
  4241.     break;
  4242.  
  4243.       case MSWIN_KEY_SCROLLDOWNPAGE:
  4244.     paint = index_scroll_down (current_index_state->lines_per_page);
  4245.     break;
  4246.  
  4247.       case MSWIN_KEY_SCROLLTO:
  4248.     paint = index_scroll_to_pos (scroll_pos + 1);
  4249.     break;
  4250.     }
  4251.  
  4252.     if(paint){
  4253.     mswin_beginupdate();
  4254.     update_titlebar_message(current_index_state->msgmap);
  4255.     update_titlebar_status(mail_elt(current_index_state->stream,
  4256.             mn_m2raw(current_index_state->msgmap,
  4257.                  mn_get_cur(current_index_state->msgmap))));
  4258.     redraw_index_body();
  4259.     mswin_endupdate();
  4260.     }
  4261.  
  4262.     return(paint);
  4263. }
  4264.  
  4265.  
  4266. /*----------------------------------------------------------------------
  4267.      MSWin scroll callback to get the text of the current message
  4268.  
  4269.   Args: title - title for new window
  4270.     text - 
  4271.     l - 
  4272.     style - 
  4273.  
  4274.   Returns: TRUE - got the requested text
  4275.        FALSE - was not able to get the requested text
  4276.  ----*/
  4277. int
  4278. index_gettext_callback(title, text, l, style)
  4279.     char  *title;
  4280.     void **text;
  4281.     long  *l;
  4282.     int   *style;
  4283. {
  4284.     ENVELOPE *env;
  4285.     BODY     *body;
  4286.     STORE_S  *so;
  4287.     gf_io_t   pc;
  4288.  
  4289.     if(mn_get_total(ps_global->msgmap) > 0L
  4290.        && (so = so_get(CharStar, NULL, WRITE_ACCESS))){
  4291.     gf_set_so_writec(&pc, so);
  4292.  
  4293.     if((env = mail_fetchstructure(ps_global->mail_stream,
  4294.                       mn_m2raw(ps_global->msgmap,
  4295.                            mn_get_cur(ps_global->msgmap)),
  4296.                       &body))
  4297.        && format_message(mn_m2raw(ps_global->msgmap,
  4298.                       mn_get_cur(ps_global->msgmap)),
  4299.                  env, body, FM_NEW_MESS, pc)){
  4300.         sprintf(title, "Folder %.50s  --  Message %ld of %ld",
  4301.             ps_global->cur_folder,
  4302.             mn_get_cur(ps_global->msgmap),
  4303.             mn_get_total(ps_global->msgmap));
  4304.         *text  = so_text(so);
  4305.         *l     = strlen((char *)so_text(so));
  4306.         *style = GETTEXT_TEXT;
  4307.  
  4308.         /* free alloc'd so, but preserve the text passed back to caller */
  4309.         so->txt = (void *)NULL;
  4310.         so_give(&so);
  4311.         return(1);
  4312.     }
  4313.     }
  4314.  
  4315.     return(0);
  4316. }
  4317. #endif    /* _WINDOWS */
  4318.